Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

508 lines
16KB

  1. <?php
  2. /**
  3. * Copyright distrib (2018)
  4. *
  5. * contact@opendistrib.net
  6. *
  7. * Ce logiciel est un programme informatique servant à aider les producteurs
  8. * à distribuer leur production en circuits courts.
  9. *
  10. * Ce logiciel est régi par la licence CeCILL soumise au droit français et
  11. * respectant les principes de diffusion des logiciels libres. Vous pouvez
  12. * utiliser, modifier et/ou redistribuer ce programme sous les conditions
  13. * de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
  14. * sur le site "http://www.cecill.info".
  15. *
  16. * En contrepartie de l'accessibilité au code source et des droits de copie,
  17. * de modification et de redistribution accordés par cette licence, il n'est
  18. * offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons,
  19. * seule une responsabilité restreinte pèse sur l'auteur du programme, le
  20. * titulaire des droits patrimoniaux et les concédants successifs.
  21. *
  22. * A cet égard l'attention de l'utilisateur est attirée sur les risques
  23. * associés au chargement, à l'utilisation, à la modification et/ou au
  24. * développement et à la reproduction du logiciel par l'utilisateur étant
  25. * donné sa spécificité de logiciel libre, qui peut le rendre complexe à
  26. * manipuler et qui le réserve donc à des développeurs et des professionnels
  27. * avertis possédant des connaissances informatiques approfondies. Les
  28. * utilisateurs sont donc invités à charger et tester l'adéquation du
  29. * logiciel à leurs besoins dans des conditions permettant d'assurer la
  30. * sécurité de leurs systèmes et ou de leurs données et, plus généralement,
  31. * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.
  32. *
  33. * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
  34. * pris connaissance de la licence CeCILL, et que vous en avez accepté les
  35. * termes.
  36. */
  37. namespace common\models;
  38. use common\helpers\GlobalParam;
  39. use common\models\PointSale;
  40. use common\models\User;
  41. use Yii;
  42. use common\components\ActiveRecordCommon;
  43. use common\models\Producer;
  44. use common\models\UserPointSale;
  45. use common\models\Order;
  46. use common\models\ProductOrder;
  47. /**
  48. * This is the model class for table "commande_auto".
  49. *
  50. * @property integer $id
  51. * @property integer $id_user
  52. * @property integer $id_producer
  53. * @property integer $id_point_sale
  54. * @property string $date_begin
  55. * @property string $date_end
  56. * @property integer $monday
  57. * @property integer $tuesday
  58. * @property integer $wednesday
  59. * @property integer $thursday
  60. * @property integer $friday
  61. * @property integer $saturday
  62. * @property integer $sunday
  63. * @property integer $week_frequency
  64. * @property string $username
  65. * @property string $auto_payment
  66. * @property string $comment
  67. */
  68. class Subscription extends ActiveRecordCommon
  69. {
  70. const AUTO_PAYMENT_DEDUCTED = 1;
  71. const AUTO_PAYMENT_YES = 2;
  72. const AUTO_PAYMENT_NO = 0;
  73. /**
  74. * @inheritdoc
  75. */
  76. public static function tableName()
  77. {
  78. return 'subscription';
  79. }
  80. /**
  81. * @inheritdoc
  82. */
  83. public function rules()
  84. {
  85. return [
  86. [['id_producer', 'id_point_sale'], 'required'],
  87. [['id_user', 'id_producer', 'id_point_sale', 'monday', 'tuesday',
  88. 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', 'week_frequency', 'auto_payment'], 'integer'],
  89. [['username', 'comment', 'date_begin', 'date_end'], 'safe'],
  90. ];
  91. }
  92. /**
  93. * @inheritdoc
  94. */
  95. public function attributeLabels()
  96. {
  97. return [
  98. 'id' => 'ID',
  99. 'id_user' => 'Utilisateur',
  100. 'id_producer' => 'Etablissement',
  101. 'id_point_sale' => 'Point de vente',
  102. 'date_begin' => 'Date de début',
  103. 'date_end' => 'Date de fin',
  104. 'monday' => 'Lundi',
  105. 'tuesday' => 'Mardi',
  106. 'wednesday' => 'Mercredi',
  107. 'thursday' => 'Jeudi',
  108. 'friday' => 'Vendredi',
  109. 'saturday' => 'Samedi',
  110. 'sunday' => 'Dimanche',
  111. 'week_frequency' => 'Périodicité',
  112. 'auto_payment' => 'Paiement automatique',
  113. 'comment' => 'Commentaire'
  114. ];
  115. }
  116. /*
  117. * Relations
  118. */
  119. public function getUser()
  120. {
  121. return $this->hasOne(User::className(), ['id' => 'id_user']);
  122. }
  123. public function getProducer()
  124. {
  125. return $this->hasOne(
  126. Producer::className(),
  127. ['id' => 'id_producer']
  128. );
  129. }
  130. public function getPointSale()
  131. {
  132. return $this->hasOne(
  133. PointSale::className(),
  134. ['id' => 'id_point_sale']
  135. );
  136. }
  137. public function getProductSubscription()
  138. {
  139. return $this->hasMany(
  140. ProductSubscription::className(),
  141. ['id_subscription' => 'id']
  142. )->with('product');
  143. }
  144. /**
  145. * Retourne les options de base nécessaires à la fonction de recherche.
  146. *
  147. * @return array
  148. */
  149. public static function defaultOptionsSearch()
  150. {
  151. return [
  152. 'with' => ['producer'],
  153. 'join_with' => ['user', 'productSubscription', 'productSubscription.product', 'pointSale'],
  154. 'orderby' => 'user.name ASC',
  155. 'attribute_id_producer' => 'subscription.id_producer'
  156. ];
  157. }
  158. /**
  159. * Ajoute la commande pour une date donnée.
  160. *
  161. * @param string $date
  162. */
  163. public function add($date, $force = false)
  164. {
  165. // distribution
  166. $now = date('Y-m-d');
  167. $distributionDate = date('Y-m-d', strtotime($date));
  168. $distribution = Distribution::searchOne([
  169. 'distribution.date' => $distributionDate
  170. ]);
  171. if ($distribution
  172. && $distribution->active
  173. && ($distributionDate > $now || $force)
  174. && count($this->productSubscription)
  175. && $this->id_point_sale) {
  176. // commande
  177. $order = new Order;
  178. if (strlen($this->username)) {
  179. $order->username = $this->username;
  180. $order->id_user = 0;
  181. } else {
  182. $order->id_user = $this->id_user;
  183. }
  184. $user = false;
  185. if ($this->id_user) {
  186. $user = User::findOne($this->id_user);
  187. }
  188. $order->date = date('Y-m-d H:i:s');
  189. $order->origin = Order::ORIGIN_AUTO;
  190. $order->id_point_sale = $this->id_point_sale;
  191. $order->id_distribution = $distribution->id;
  192. $order->id_subscription = $this->id;
  193. $order->status = 'tmp-order';
  194. if (strlen($this->comment)) {
  195. $order->comment = $this->comment;
  196. }
  197. $pointSale = PointSale::findOne($this->id_point_sale);
  198. if ($pointSale) {
  199. $order->auto_payment = 0;
  200. if($this->auto_payment == self::AUTO_PAYMENT_DEDUCTED) {
  201. $order->auto_payment = $order->isCreditAutoPayment();
  202. }
  203. elseif($this->auto_payment == self::AUTO_PAYMENT_YES) {
  204. $order->auto_payment = 1;
  205. }
  206. elseif($this->auto_payment == self::AUTO_PAYMENT_NO) {
  207. $order->auto_payment = 0;
  208. }
  209. $order->tiller_synchronization = $order->auto_payment;
  210. $userPointSale = UserPointSale::searchOne([
  211. 'id_point_sale' => $this->id_point_sale,
  212. 'id_user' => $this->id_user
  213. ]);
  214. if ($userPointSale && strlen($userPointSale->comment)) {
  215. $order->comment_point_sale = $userPointSale->comment;
  216. }
  217. $order->save();
  218. // liaison utilisateur / point de vente
  219. if ($order->id_user) {
  220. $pointSale = PointSale::findOne($this->id_point_sale);
  221. $pointSale->linkUser($order->id_user);
  222. }
  223. // produits
  224. $productsAdd = false;
  225. foreach ($this->productSubscription as $productSubscription) {
  226. $productOrder = new ProductOrder;
  227. $productOrder->id_order = $order->id;
  228. $productOrder->id_product = $productSubscription->product->id;
  229. $productOrder->quantity = $productSubscription->quantity;
  230. $productOrder->price = $productSubscription->product->getPrice([
  231. 'user' => $user,
  232. 'point_sale' => $pointSale,
  233. 'quantity' => $productSubscription->quantity
  234. ]);
  235. $productOrder->unit = $productSubscription->product->unit;
  236. $productOrder->step = $productSubscription->product->step;
  237. $productOrder->id_tax_rate = $productSubscription->product->taxRate->id;
  238. $productOrder->save();
  239. $productsAdd = true;
  240. }
  241. if (!$productsAdd) {
  242. $order->delete();
  243. }
  244. $order->initReference();
  245. }
  246. }
  247. }
  248. /**
  249. * Ajoute les commandes pour une date donnée à partir des abonnements.
  250. *
  251. * @param string $date
  252. * @param boolean $force
  253. */
  254. public static function addAll($date, $force = false)
  255. {
  256. $distribution = Distribution::searchOne([
  257. 'date' => date('Y-m-d', strtotime($date)),
  258. 'id_producer' => GlobalParam::getCurrentProducerId(),
  259. ]);
  260. if ($distribution) {
  261. $arrayOrdersDistribution = Order::searchAll([
  262. Order::tableName() . '.id_distribution' => $distribution->id
  263. ]);
  264. $arraySubscriptions = self::searchByDate($date);
  265. foreach ($arraySubscriptions as $subscription) {
  266. if (!$subscription->hasOrderAlreadyExist($arrayOrdersDistribution)) {
  267. $subscription->add($date, $force);
  268. }
  269. }
  270. }
  271. }
  272. /**
  273. * Informe s'il existe une commande correspond à l'abonnement courant.
  274. *
  275. * @param array $arrayOrders
  276. * @return boolean
  277. */
  278. public function hasOrderAlreadyExist($arrayOrders)
  279. {
  280. if (is_array($arrayOrders) && count($arrayOrders) > 0) {
  281. foreach ($arrayOrders as $order) {
  282. if ((($order->id_user > 0 && $order->id_user == $this->id_user) ||
  283. (!$order->id_user && $order->username == $this->username)) &&
  284. $order->id_point_sale == $this->id_point_sale) {
  285. return true;
  286. }
  287. }
  288. }
  289. return false;
  290. }
  291. /**
  292. * Retourne les abonnements pour une date donnée.
  293. *
  294. * @param string $date
  295. * @return array
  296. */
  297. public static function searchByDate($date)
  298. {
  299. $date = date('Y-m-d', strtotime($date));
  300. $subscriptions = Subscription::searchAll();
  301. $arrSubscriptions = [];
  302. foreach ($subscriptions as $s) {
  303. if ($date >= $s->date_begin &&
  304. (!$s->date_end || $date <= $s->date_end) &&
  305. $s->matchWith($date)) {
  306. $arrSubscriptions[] = $s;
  307. }
  308. }
  309. return $arrSubscriptions;
  310. }
  311. /**
  312. * Valide le fait qu'un abonnement est bien compatible avec une date donnée.
  313. *
  314. * @param string $date
  315. * @return boolean
  316. */
  317. public function matchWith($date)
  318. {
  319. $arrayDays = [
  320. 1 => 'monday',
  321. 2 => 'tuesday',
  322. 3 => 'wednesday',
  323. 4 => 'thursday',
  324. 5 => 'friday',
  325. 6 => 'saturday',
  326. 7 => 'sunday'
  327. ];
  328. $nbDays = (strtotime($date) - strtotime($this->date_begin)) / (24 * 60 * 60);
  329. if (round($nbDays) % ($this->week_frequency * 7) < 7) {
  330. $numDay = date('N', strtotime($date));
  331. $day = $arrayDays[$numDay];
  332. if ($this->$day) {
  333. return true;
  334. }
  335. }
  336. return false;
  337. }
  338. /**
  339. * Recherche les distributions futures où l'abonnement peut s'appliquer.
  340. *
  341. * @return array
  342. */
  343. public function searchMatchedIncomingDistributions()
  344. {
  345. $params = [
  346. ':date_earliest_order' => date('Y-m-d'),
  347. ':date_begin' => date('Y-m-d', strtotime($this->date_begin)),
  348. ':id_producer' => GlobalParam::getCurrentProducerId()
  349. ];
  350. $incomingDistributions = Distribution::find()
  351. ->where('id_producer = :id_producer')
  352. ->andWhere('date >= :date_begin')
  353. ->andWhere('date > :date_earliest_order');
  354. if ($this->date_end) {
  355. $incomingDistributions->andWhere('date <= :date_end');
  356. $params[':date_end'] = date('Y-m-d', strtotime($this->date_end));
  357. }
  358. $incomingDistributions->orderBy('date ASC');
  359. $incomingDistributions->params($params);
  360. $incomingDistributionsArray = $incomingDistributions->all();
  361. Distribution::filterDistributionsByDateDelay($incomingDistributionsArray);
  362. $matchedIncomingDistributionsArray = [];
  363. foreach ($incomingDistributionsArray as $incomingDistribution) {
  364. if ($this->matchWith($incomingDistribution->date)) {
  365. $matchedIncomingDistributionsArray[] = $incomingDistribution;
  366. }
  367. }
  368. return $matchedIncomingDistributionsArray;
  369. }
  370. public function deleteOrdersIncomingDistributions($deleteAfterDateEnd = false)
  371. {
  372. $dateStart = $this->date_begin;
  373. $comparatorDateStart = '>=';
  374. if($deleteAfterDateEnd) {
  375. $dateStart = $this->date_end;
  376. $comparatorDateStart = '>';
  377. }
  378. $params = [
  379. ':id_producer' => GlobalParam::getCurrentProducerId(),
  380. ':date_today' => date('Y-m-d'),
  381. ':date_start' => $dateStart,
  382. ':id_subscription' => $this->id
  383. ];
  384. $orderDeadline = Producer::getConfig('order_deadline');
  385. $hour = date('G');
  386. if ($hour >= $orderDeadline) {
  387. $conditionDistributionDate = 'distribution.date > :date_today';
  388. } else {
  389. $conditionDistributionDate = 'distribution.date >= :date_today';
  390. }
  391. $orders = Order::find()
  392. ->joinWith('distribution')
  393. ->where('distribution.id_producer = :id_producer')
  394. ->andWhere($conditionDistributionDate)
  395. ->andWhere('distribution.date '.$comparatorDateStart.' :date_start')
  396. ->andWhere('order.id_subscription = :id_subscription');
  397. $orders->params($params);
  398. $ordersArray = $orders->all();
  399. $configCredit = Producer::getConfig('credit');
  400. $countOrdersDeleted = 0;
  401. if ($ordersArray && count($ordersArray)) {
  402. foreach ($ordersArray as $order) {
  403. $theOrder = Order::searchOne(['id' => $order->id]);
  404. // remboursement de la commande
  405. if ($theOrder->id_user && $theOrder->getAmount(Order::AMOUNT_PAID) && $configCredit) {
  406. $theOrder->saveCreditHistory(
  407. CreditHistory::TYPE_REFUND,
  408. $theOrder->getAmount(Order::AMOUNT_PAID),
  409. $theOrder->distribution->id_producer,
  410. $theOrder->id_user,
  411. User::getCurrentId()
  412. );
  413. }
  414. $order->delete(true);
  415. $countOrdersDeleted ++;
  416. }
  417. }
  418. return $countOrdersDeleted;
  419. }
  420. public function updateIncomingDistributions($update = false)
  421. {
  422. $matchedDistributionsArray = $this->searchMatchedIncomingDistributions();
  423. if ($update) {
  424. $this->deleteOrdersIncomingDistributions();
  425. }
  426. if (count($matchedDistributionsArray)) {
  427. foreach ($matchedDistributionsArray as $distribution) {
  428. $this->add($distribution->date);
  429. }
  430. }
  431. }
  432. public function getUsername()
  433. {
  434. if ($this->user) {
  435. return $this->user->getUsername();
  436. }
  437. return $this->username;
  438. }
  439. }