You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Order.php 36KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094
  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\Debug;
  39. use common\helpers\GlobalParam;
  40. use common\helpers\Price;
  41. use common\models\Producer;
  42. use Yii;
  43. use yii\helpers\Html;
  44. use common\components\ActiveRecordCommon;
  45. use yii\web\NotFoundHttpException;
  46. /**
  47. * This is the model class for table "order".
  48. *
  49. * @property integer $id
  50. * @property integer $id_user
  51. * @property string $date
  52. * @property string $date_update
  53. * @property integer $id_point_sale
  54. * @property integer $id_distribution
  55. * @property boolean $auto_payment
  56. * @property integer $id_subscription
  57. */
  58. class Order extends ActiveRecordCommon
  59. {
  60. var $amount = 0;
  61. var $amount_with_tax = 0;
  62. var $amount_vat = [];
  63. var $invoice_amount = 0;
  64. var $invoice_amount_with_tax = 0;
  65. var $invoice_amount_vat = [];
  66. var $paid_amount = 0;
  67. var $weight = 0;
  68. const ORIGIN_AUTO = 'auto';
  69. const ORIGIN_USER = 'user';
  70. const ORIGIN_ADMIN = 'admin';
  71. const PAYMENT_PAID = 'paid';
  72. const PAYMENT_UNPAID = 'unpaid';
  73. const PAYMENT_SURPLUS = 'surplus';
  74. const INVOICE_AMOUNT_TOTAL = 'invoice-total';
  75. const AMOUNT_TOTAL = 'total';
  76. const AMOUNT_PAID = 'paid';
  77. const AMOUNT_REMAINING = 'remaining';
  78. const AMOUNT_SURPLUS = 'surplus';
  79. const STATE_OPEN = 'open';
  80. const STATE_PREPARATION = 'preparation';
  81. const STATE_DELIVERED = 'delivered';
  82. /**
  83. * @inheritdoc
  84. */
  85. public static function tableName()
  86. {
  87. return 'order';
  88. }
  89. /**
  90. * @inheritdoc
  91. */
  92. public function rules()
  93. {
  94. return [
  95. [['id_user', 'date', 'status'], 'required', 'message' => ''],
  96. [
  97. [
  98. 'id_user',
  99. 'id_point_sale',
  100. 'id_distribution',
  101. 'id_subscription',
  102. 'id_invoice',
  103. 'id_quotation',
  104. 'id_delivery_note'
  105. ],
  106. 'integer'
  107. ],
  108. [['auto_payment', 'tiller_synchronization', 'delivery_home'], 'boolean'],
  109. [['status', 'reference', 'delivery_address', 'online_payment_url', 'tiller_external_id'], 'string'],
  110. [['date', 'date_update', 'comment', 'comment_point_sale', 'mean_payment', 'tiller_external_id'], 'safe']
  111. ];
  112. }
  113. /**
  114. * @inheritdoc
  115. */
  116. public function attributeLabels()
  117. {
  118. return [
  119. 'id' => 'ID',
  120. 'id_user' => 'Id User',
  121. 'date' => 'Date',
  122. 'date_update' => 'Date de modification',
  123. 'id_point_sale' => 'Point de vente',
  124. 'id_distribution' => 'Date de distribution',
  125. 'id_subscription' => 'Abonnement',
  126. 'status' => 'Statut',
  127. 'id_invoice' => 'Facture',
  128. 'id_quotation' => 'Devis',
  129. 'id_delivery_note' => 'Bon de livraison',
  130. 'reference' => 'Référence',
  131. 'delivery_home' => 'Livraison à domicile',
  132. 'delivery_address' => 'Adresse de livraison',
  133. 'online_payment_url' => 'URL de paiement',
  134. 'tiller_external_id' => 'Tiller : externalId',
  135. ];
  136. }
  137. /*
  138. * Relations
  139. */
  140. public function getUser()
  141. {
  142. return $this->hasOne(User::className(), ['id' => 'id_user']);
  143. }
  144. public function getProductOrder()
  145. {
  146. return $this->hasMany(ProductOrder::className(), ['id_order' => 'id'])
  147. ->orderBy(['product.order' => SORT_ASC])
  148. ->joinWith('product');
  149. }
  150. public function getDistribution()
  151. {
  152. return $this->hasOne(Distribution::className(), ['id' => 'id_distribution'])
  153. ->with('producer');
  154. }
  155. public function getPointSale()
  156. {
  157. return $this->hasOne(PointSale::className(), ['id' => 'id_point_sale'])
  158. ->with('userPointSale');
  159. }
  160. public function getCreditHistory()
  161. {
  162. return $this->hasMany(CreditHistory::className(), ['id_order' => 'id']);
  163. }
  164. public function getSubscription()
  165. {
  166. return $this->hasOne(Subscription::className(), ['id' => 'id_subscription'])
  167. ->with('productSubscription');
  168. }
  169. public function getInvoice()
  170. {
  171. return $this->hasOne(Invoice::className(), ['id' => 'id_invoice']);
  172. }
  173. public function getQuotation()
  174. {
  175. return $this->hasOne(Quotation::className(), ['id' => 'id_quotation']);
  176. }
  177. public function getDeliveryNote()
  178. {
  179. return $this->hasOne(DeliveryNote::className(), ['id' => 'id_delivery_note']);
  180. }
  181. /**
  182. * Retourne les options de base nécessaires à la fonction de recherche.
  183. *
  184. * @return array
  185. */
  186. public static function defaultOptionsSearch()
  187. {
  188. return [
  189. 'with' => [
  190. 'productOrder',
  191. 'productOrder.product',
  192. 'creditHistory',
  193. 'creditHistory.userAction',
  194. 'pointSale'
  195. ],
  196. 'join_with' => ['distribution', 'user', 'user.userProducer'],
  197. 'orderby' => 'order.date ASC',
  198. 'attribute_id_producer' => 'distribution.id_producer'
  199. ];
  200. }
  201. /**
  202. * Initialise le montant total, le montant déjà payé et le poids de la
  203. * commande.
  204. */
  205. public function init($taxCalculationMethod = Document::TAX_CALCULATION_METHOD_DEFAULT)
  206. {
  207. $this->initAmount($taxCalculationMethod);
  208. $this->initPaidAmount();
  209. return $this;
  210. }
  211. /**
  212. * Initialise le montant de la commande.
  213. *
  214. */
  215. public function initAmount($taxCalculationMethod = Document::TAX_CALCULATION_METHOD_DEFAULT)
  216. {
  217. $this->amount = 0;
  218. $this->amount_with_tax = 0;
  219. $this->amount_vat = [];
  220. $this->invoice_amount = 0;
  221. $this->invoice_amount_with_tax = 0;
  222. $this->invoice_amount_vat = [];
  223. $this->weight = 0;
  224. if (isset($this->productOrder)) {
  225. foreach ($this->productOrder as $productOrder) {
  226. $this->addAmount(self::AMOUNT_TOTAL, $productOrder, $taxCalculationMethod);
  227. $this->addAmount(self::INVOICE_AMOUNT_TOTAL, $productOrder, $taxCalculationMethod);
  228. $this->addWeight($productOrder);
  229. }
  230. }
  231. }
  232. public function addWeight($productOrder)
  233. {
  234. if ($productOrder->unit == 'piece') {
  235. if (isset($productOrder->product)) {
  236. $this->weight += ($productOrder->quantity * $productOrder->product->weight) / 1000;
  237. }
  238. } else {
  239. $this->weight += $productOrder->quantity;
  240. }
  241. }
  242. public function addAmount($typeTotal, $productOrder, $taxCalculationMethod)
  243. {
  244. $fieldNameAmount = $this->getFieldNameAmount($typeTotal);
  245. $fieldNameAmountWithTax = $this->getFieldNameAmount($typeTotal, 'with_tax');
  246. $price = $productOrder->getPriceByTypeTotal($typeTotal);
  247. $this->$fieldNameAmount += $price * $productOrder->quantity;
  248. $this->$fieldNameAmountWithTax += Price::getPriceWithTax(
  249. $price,
  250. $productOrder->taxRate->value,
  251. $taxCalculationMethod
  252. ) * $productOrder->quantity;
  253. $this->addVat($typeTotal, $price * $productOrder->quantity, $productOrder->taxRate, $taxCalculationMethod);
  254. }
  255. public function addVat($typeTotal, $priceTotalWithoutTax, $taxRate, $taxCalculationMethod)
  256. {
  257. $fieldName = $this->getFieldNameAmount($typeTotal, 'vat');
  258. if(!isset($this->$fieldName[$taxRate->id])) {
  259. $this->$fieldName[$taxRate->id] = 0;
  260. }
  261. $this->$fieldName[$taxRate->id] += Price::getVat($priceTotalWithoutTax, $taxRate->value, $taxCalculationMethod);
  262. }
  263. public function getTotalVat($typeTotal = self::AMOUNT_TOTAL)
  264. {
  265. $fieldName = $this->getFieldNameAmount($typeTotal, 'vat');
  266. $totalVat = 0;
  267. foreach($this->$fieldName as $vat) {
  268. $totalVat += $vat;
  269. }
  270. return $totalVat;
  271. }
  272. public function getFieldNameAmount($typeTotal = self::AMOUNT_TOTAL, $typeField = '')
  273. {
  274. $fieldName = 'amount';
  275. if($typeTotal == self::INVOICE_AMOUNT_TOTAL) {
  276. $fieldName = 'invoice_amount';
  277. }
  278. if($typeField == 'vat') {
  279. $fieldName = $fieldName.'_vat';
  280. }
  281. elseif($typeField == 'with_tax') {
  282. $fieldName = $fieldName.'_with_tax';
  283. }
  284. return $fieldName;
  285. }
  286. /**
  287. * Initialise le montant payé de la commande et le retourne.
  288. *
  289. * @return float
  290. */
  291. public function initPaidAmount()
  292. {
  293. if (isset($this->creditHistory)) {
  294. $history = $this->creditHistory;
  295. } else {
  296. $history = CreditHistory::find()
  297. ->where(['id_order' => $this->id])
  298. ->all();
  299. }
  300. $this->paid_amount = 0;
  301. if (count($history)) {
  302. foreach ($history as $ch) {
  303. if ($ch->type == CreditHistory::TYPE_PAYMENT) {
  304. $this->paid_amount += $ch->amount;
  305. } elseif ($ch->type == CreditHistory::TYPE_REFUND) {
  306. $this->paid_amount -= $ch->amount;
  307. }
  308. }
  309. }
  310. }
  311. public function delete()
  312. {
  313. // remboursement si l'utilisateur a payé pour cette commande
  314. $amountPaid = $this->getAmount(Order::AMOUNT_PAID);
  315. if ($amountPaid > 0.01) {
  316. $this->saveCreditHistory(
  317. CreditHistory::TYPE_REFUND,
  318. $amountPaid,
  319. GlobalParam::getCurrentProducerId(),
  320. $this->id_user,
  321. User::getCurrentId()
  322. );
  323. }
  324. // delete
  325. if (Producer::getConfig('option_behavior_cancel_order') == Producer::BEHAVIOR_DELETE_ORDER_DELETE ||
  326. (Producer::getConfig(
  327. 'option_behavior_cancel_order'
  328. ) == Producer::BEHAVIOR_DELETE_ORDER_STATUS && strlen($this->date_delete))) {
  329. ProductOrder::deleteAll(['id_order' => $this->id]);
  330. return parent::delete();
  331. } // status 'delete'
  332. elseif (Producer::getConfig('option_behavior_cancel_order') == Producer::BEHAVIOR_DELETE_ORDER_STATUS) {
  333. $this->date_delete = date('Y-m-d H:i:s');
  334. return $this->save();
  335. }
  336. }
  337. /**
  338. * Changement de statut d'une commande
  339. *
  340. * @param $newStatus
  341. */
  342. public function changeOrderStatus($newStatus, $origin)
  343. {
  344. $orderStatusArray = GlobalParam::get('orderStatus');
  345. switch ($newStatus) {
  346. case 'new-order' :
  347. $this->addOrderStatusHistory($newStatus, $origin);
  348. $this->status = $newStatus;
  349. $this->save();
  350. break;
  351. case 'waiting-paiement-on-delivery':
  352. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  353. $this->addOrderStatusHistory($newStatus, $origin);
  354. $this->status = $newStatus;
  355. $this->save();
  356. }
  357. break;
  358. case 'waiting-paiement-by-credit':
  359. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  360. $this->addOrderStatusHistory($newStatus, $origin);
  361. $this->status = $newStatus;
  362. $this->save();
  363. }
  364. break;
  365. case 'paid-by-credit':
  366. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  367. $this->addOrderStatusHistory($newStatus, $origin);
  368. $this->status = $newStatus;
  369. $this->save();
  370. }
  371. break;
  372. case 'waiting-delevery' :
  373. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  374. $this->addOrderStatusHistory($newStatus, $origin);
  375. $this->status = $newStatus;
  376. $this->save();
  377. }
  378. break;
  379. case 'delivered':
  380. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  381. $this->addOrderStatusHistory($newStatus, $origin);
  382. $this->status = $newStatus;
  383. $this->save();
  384. }
  385. break;
  386. case 'refunded':
  387. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  388. $this->addOrderStatusHistory($newStatus, $origin);
  389. $this->status = $newStatus;
  390. $this->save();
  391. }
  392. break;
  393. case 'cancel':
  394. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  395. $this->addOrderStatusHistory($newStatus, $origin);
  396. $this->status = $newStatus;
  397. $this->save();
  398. }
  399. break;
  400. default:
  401. throw new NotFoundHttpException('Statut de commande inconnu.');
  402. break;
  403. }
  404. }
  405. public function addOrderStatusHistory($newStatus, $origin)
  406. {
  407. $orderStatusHistory = new OrderStatusHistory();
  408. $orderStatusHistory->id_user = User::getCurrentId();
  409. $orderStatusHistory->id_order = $this->id;
  410. $orderStatusHistory->status = $newStatus;
  411. $orderStatusHistory->origin = $origin;
  412. $orderStatusHistory->date = date('Y-m-d H:i:s');
  413. $orderStatusHistory->save();
  414. }
  415. /**
  416. * Retourne le montant de la commande (total, payé, restant, ou en surplus).
  417. *
  418. * @param boolean $format
  419. * @return float
  420. */
  421. public function getAmount($type = self::AMOUNT_TOTAL, $format = false)
  422. {
  423. $amount = $this->amount;
  424. if ($type == self::INVOICE_AMOUNT_TOTAL && $this->invoice_amount) {
  425. $amount = $this->invoice_amount;
  426. }
  427. return $this->_getAmountGeneric($type, $amount, $format);
  428. }
  429. public function getAmountWithTax($type = self::AMOUNT_TOTAL, $format = false)
  430. {
  431. $amount = $this->amount_with_tax;
  432. if ($type == self::INVOICE_AMOUNT_TOTAL && $this->invoice_amount) {
  433. $amount = $this->invoice_amount_with_tax;
  434. }
  435. return $this->_getAmountGeneric($type, $amount, $format);
  436. }
  437. protected function _getAmountGeneric($type, $amountOrder, $format)
  438. {
  439. switch ($type) {
  440. case self::AMOUNT_TOTAL :
  441. case self::INVOICE_AMOUNT_TOTAL :
  442. $amount = $amountOrder;
  443. break;
  444. case self::AMOUNT_PAID :
  445. $this->initPaidAmount();
  446. $amount = $this->paid_amount;
  447. break;
  448. case self::AMOUNT_REMAINING :
  449. $amount = $this->getAmountWithTax(self::AMOUNT_TOTAL)
  450. - $this->getAmountWithTax(self::AMOUNT_PAID);
  451. break;
  452. case self::AMOUNT_SURPLUS :
  453. $amount = $this->getAmountWithTax(self::AMOUNT_PAID)
  454. - $this->getAmountWithTax(self::AMOUNT_TOTAL);
  455. break;
  456. }
  457. if ($format) {
  458. return Price::format($amount);
  459. } else {
  460. return $amount;
  461. }
  462. }
  463. /**
  464. * Retourne les informations relatives à la commande au format JSON.
  465. *
  466. * @return string
  467. */
  468. public function getDataJson()
  469. {
  470. $order = Order::searchOne(['order.id' => $this->id]);
  471. $jsonOrder = [];
  472. if ($order) {
  473. $jsonOrder = [
  474. 'products' => [],
  475. 'amount' => $order->amount,
  476. 'str_amount' => $order->getAmountWithTax(self::AMOUNT_TOTAL, true),
  477. 'paid_amount' => $order->getAmount(self::AMOUNT_PAID),
  478. 'comment' => $order->comment,
  479. ];
  480. foreach ($order->productOrder as $productOrder) {
  481. $jsonOrder['products'][$productOrder->id_product] = $productOrder->quantity;
  482. }
  483. }
  484. return json_encode($jsonOrder);
  485. }
  486. /**
  487. * Enregistre un modèle de type CreditHistory.
  488. *
  489. * @param string $type
  490. * @param float $montant
  491. * @param integer $idProducer
  492. * @param integer $idUser
  493. * @param integer $idUserAction
  494. */
  495. public function saveCreditHistory($type, $amount, $idProducer, $idUser, $idUserAction)
  496. {
  497. $creditHistory = new CreditHistory;
  498. $creditHistory->id_user = $this->id_user;
  499. $creditHistory->id_order = $this->id;
  500. $creditHistory->amount = $amount;
  501. $creditHistory->type = $type;
  502. $creditHistory->id_producer = $idProducer;
  503. $creditHistory->id_user_action = $idUserAction;
  504. $creditHistory->populateRelation('order', $this);
  505. $creditHistory->populateRelation('user', User::find()->where(['id' => $this->id_user])->one());
  506. $creditHistory->save();
  507. }
  508. /**
  509. * Ajuste le crédit pour que la commande soit payée.
  510. *
  511. * @return boolean
  512. */
  513. public function processCredit()
  514. {
  515. if ($this->id_user) {
  516. $paymentStatus = $this->getPaymentStatus();
  517. if ($paymentStatus == self::PAYMENT_PAID) {
  518. return true;
  519. } elseif ($paymentStatus == self::PAYMENT_SURPLUS) {
  520. $type = CreditHistory::TYPE_REFUND;
  521. $amount = $this->getAmount(self::AMOUNT_SURPLUS);
  522. } elseif ($paymentStatus == self::PAYMENT_UNPAID) {
  523. $type = CreditHistory::TYPE_PAYMENT;
  524. $amount = $this->getAmount(self::AMOUNT_REMAINING);
  525. }
  526. $this->saveCreditHistory(
  527. $type,
  528. $amount,
  529. GlobalParam::getCurrentProducerId(),
  530. $this->id_user,
  531. User::getCurrentId()
  532. );
  533. }
  534. }
  535. public function setTillerSynchronization()
  536. {
  537. $order = Order::searchOne(['id' => $this->id]);
  538. $paymentStatus = $order->getPaymentStatus();
  539. if ($paymentStatus == self::PAYMENT_PAID) {
  540. $order->tiller_synchronization = 1;
  541. } else {
  542. $order->tiller_synchronization = 0;
  543. }
  544. $order->save();
  545. return $order;
  546. }
  547. /**
  548. * Retourne le statut de paiement de la commande (payée, surplus, ou impayée).
  549. *
  550. * @return string
  551. */
  552. public function getPaymentStatus()
  553. {
  554. // payé
  555. if ($this->getAmountWithtax() - $this->getAmount(self::AMOUNT_PAID) < 0.01 &&
  556. $this->getAmountWithtax() - $this->getAmount(self::AMOUNT_PAID) > -0.01) {
  557. return self::PAYMENT_PAID;
  558. } // à rembourser
  559. elseif ($this->getAmountWithtax() - $this->getAmount(self::AMOUNT_PAID) <= -0.01) {
  560. return self::PAYMENT_SURPLUS;
  561. } // reste à payer
  562. elseif ($this->getAmountWithtax() - $this->getAmount(self::AMOUNT_PAID) >= 0.01) {
  563. return self::PAYMENT_UNPAID;
  564. }
  565. }
  566. /**
  567. * Retourne le résumé du panier au format HTML.
  568. *
  569. * @return string
  570. */
  571. public function getCartSummary($htmlFormat = true)
  572. {
  573. if (!isset($this->productOrder)) {
  574. $this->productOrder = productOrder::find()->where(['id_order' => $this->id])->all();
  575. }
  576. $html = '';
  577. $count = count($this->productOrder);
  578. $i = 0;
  579. foreach ($this->productOrder as $p) {
  580. if (isset($p->product)) {
  581. $html .= Html::encode($p->product->name) . ' (' . $p->quantity . '&nbsp;' . Product::strUnit(
  582. $p->unit,
  583. 'wording_short',
  584. true
  585. ) . ')';
  586. if (++$i != $count) {
  587. if ($htmlFormat) {
  588. $html .= '<br />';
  589. } else {
  590. $html .= "\n";
  591. }
  592. }
  593. }
  594. }
  595. return $html;
  596. }
  597. /**
  598. * Retourne le résumé du point de vente lié à la commande au format HTML.
  599. *
  600. * @return string
  601. */
  602. public function getPointSaleSummary()
  603. {
  604. $html = '';
  605. if (isset($this->pointSale)) {
  606. $html .= '<span class="name-point-sale">' .
  607. Html::encode($this->pointSale->name) .
  608. '</span>' .
  609. '<br /><span class="locality">'
  610. . Html::encode($this->pointSale->locality)
  611. . '</span>';
  612. if (strlen($this->comment_point_sale)) {
  613. $html .= '<div class="comment"><span>'
  614. . Html::encode($this->comment_point_sale)
  615. . '</span></div>';
  616. }
  617. } else {
  618. $html .= 'Point de vente supprimé';
  619. }
  620. return $html;
  621. }
  622. /**
  623. * Retourne le résumé du paiement (montant, statut).
  624. *
  625. * @return string
  626. */
  627. public function getAmountSummary()
  628. {
  629. $html = '';
  630. $creditActive = Producer::getConfig('credit');
  631. $html .= $this->getAmountWithTax(self::AMOUNT_TOTAL, true);
  632. if ($creditActive) {
  633. $html .= '<br />';
  634. if ($this->paid_amount) {
  635. if ($this->getPaymentStatus() == Order::PAYMENT_PAID) {
  636. $html .= '<span class="label label-success">Payée</span>';
  637. } elseif ($this->getPaymentStatus() == Order::PAYMENT_UNPAID) {
  638. $html .= '<span class="label label-danger">Non payée</span><br />
  639. Reste <strong>' . $this->getAmount(
  640. Order::AMOUNT_REMAINING,
  641. true
  642. ) . '</strong> à payer';
  643. } elseif ($this->getPaymentStatus() == Order::PAYMENT_SURPLUS) {
  644. $html .= '<span class="label label-success">Payée</span>';
  645. }
  646. } else {
  647. $html .= '<span class="label label-default">Non réglé</span>';
  648. }
  649. }
  650. return $html;
  651. }
  652. /**
  653. * Retourne une chaine de caractère décrivant l'utilisateur lié à la commande.
  654. *
  655. * @return string
  656. */
  657. public function getStrUser()
  658. {
  659. if (isset($this->user)) {
  660. if (isset($this->user->name_legal_person) && strlen($this->user->name_legal_person)) {
  661. return Html::encode($this->user->name_legal_person);
  662. } else {
  663. $strUser = $this->user->lastname;
  664. if($this->user->lastname && $this->user->name) {
  665. $strUser .= ' '.$this->user->name;
  666. }
  667. return Html::encode($strUser);
  668. }
  669. } elseif (strlen($this->username)) {
  670. return Html::encode($this->username);
  671. } else {
  672. return 'Client introuvable';
  673. }
  674. }
  675. /**
  676. * Retourne l'état de la commande (livrée, modifiable ou en préparation)
  677. *
  678. * @return string
  679. */
  680. public function getState()
  681. {
  682. $orderDate = strtotime($this->distribution->date);
  683. $today = strtotime(date('Y-m-d'));
  684. $todayHour = date('G');
  685. $dayDistribution = strtolower(date('l', strtotime($this->distribution->date)));
  686. $orderDelay = Producer::getConfig(
  687. 'order_delay',
  688. $this->distribution->id_producer
  689. );
  690. $orderDelaySpecific = Producer::getConfig(
  691. 'order_delay_' . $dayDistribution,
  692. $this->distribution->id_producer
  693. );
  694. if ($orderDelaySpecific) {
  695. $orderDelay = $orderDelaySpecific;
  696. }
  697. $orderDeadline = Producer::getConfig(
  698. 'order_deadline',
  699. $this->distribution->id_producer
  700. );
  701. $orderDeadlineSpecific = Producer::getConfig(
  702. 'order_deadline_' . $dayDistribution,
  703. $this->distribution->id_producer
  704. );
  705. if ($orderDeadlineSpecific) {
  706. $orderDeadline = $orderDeadlineSpecific;
  707. }
  708. $nbDays = (int)round((($orderDate - $today) / (24 * 60 * 60)));
  709. if ($nbDays <= 0) {
  710. return self::STATE_DELIVERED;
  711. } elseif ($nbDays >= $orderDelay &&
  712. ($nbDays != $orderDelay ||
  713. ($nbDays == $orderDelay && $todayHour < $orderDeadline))) {
  714. return self::STATE_OPEN;
  715. }
  716. return self::STATE_PREPARATION;
  717. }
  718. /**
  719. * Retourne l'origine de la commande (client, automatique ou admin) sous forme
  720. * texte ou HTML.
  721. *
  722. * @param boolean $with_label
  723. * @return string
  724. */
  725. public function getStrOrigin($withLabel = false)
  726. {
  727. $classLabel = '';
  728. $str = '';
  729. if ($this->origin == self::ORIGIN_USER) {
  730. $classLabel = 'success';
  731. $str = 'Client';
  732. } elseif ($this->origin == self::ORIGIN_AUTO) {
  733. $classLabel = 'default';
  734. $str = 'Auto';
  735. } elseif ($this->origin == self::ORIGIN_ADMIN) {
  736. $classLabel = 'warning';
  737. $str = 'Vous';
  738. }
  739. if ($withLabel) {
  740. return '<span class="label label-' . $classLabel . '">'
  741. . $str . '</span>';
  742. } else {
  743. return $str;
  744. }
  745. }
  746. /**
  747. * Retourne l'historique de la commande (ajoutée, modifiée, supprimée) au
  748. * format HTML.
  749. *
  750. * @return string
  751. */
  752. public function getStrHistory()
  753. {
  754. $arr = [
  755. 'class' => 'create',
  756. 'glyphicon' => 'plus',
  757. 'str' => 'Ajoutée',
  758. 'date' => $this->date
  759. ];
  760. if (!is_null($this->date_update)) {
  761. $arr = [
  762. 'class' => 'update',
  763. 'glyphicon' => 'pencil',
  764. 'str' => 'Modifiée',
  765. 'date' => $this->date_update
  766. ];
  767. }
  768. if (!is_null($this->date_delete)) {
  769. $arr = [
  770. 'class' => 'delete',
  771. 'glyphicon' => 'remove',
  772. 'str' => 'Annulée',
  773. 'date' => $this->date_delete
  774. ];
  775. }
  776. $html = '<div class="small"><span class="' . $arr['class'] . '">'
  777. . '<span class="glyphicon glyphicon-' . $arr['glyphicon'] . '"></span> '
  778. . $arr['str'] . '</span> le <strong>'
  779. . date('d/m/Y à G\hi', strtotime($arr['date'])) . '</strong></div>';
  780. return $html;
  781. }
  782. /**
  783. * Retourne une classe identifiant l'historique de la commande (ajoutée,
  784. * modifiée, supprimée).
  785. *
  786. * @return string
  787. */
  788. public function getClassHistory()
  789. {
  790. if (!is_null($this->date_delete)) {
  791. return 'commande-delete';
  792. }
  793. if (!is_null($this->date_update)) {
  794. return 'commande-update';
  795. }
  796. return 'commande-create';
  797. }
  798. /**
  799. * Retourne la quantité d'un produit donné de plusieurs commandes.
  800. *
  801. * @param integer $idProduct
  802. * @param array $orders
  803. * @param boolean $ignoreCancel
  804. *
  805. * @return integer
  806. */
  807. public static function getProductQuantity($idProduct, $orders, $ignoreCancel = false, $unit = null)
  808. {
  809. $quantity = 0;
  810. if (isset($orders) && is_array($orders) && count($orders)) {
  811. foreach ($orders as $c) {
  812. if (is_null($c->date_delete) || $ignoreCancel) {
  813. foreach ($c->productOrder as $po) {
  814. if ($po->id_product == $idProduct &&
  815. ((is_null($unit) && $po->product->unit == $po->unit) || (!is_null($unit) && strlen(
  816. $unit
  817. ) && $po->unit == $unit))) {
  818. $quantity += $po->quantity;
  819. }
  820. }
  821. }
  822. }
  823. }
  824. return $quantity;
  825. }
  826. public static function getProductQuantityPieces($idProduct, $orders)
  827. {
  828. $quantity = 0;
  829. if (isset($orders) && is_array($orders) && count($orders)) {
  830. foreach ($orders as $c) {
  831. if (is_null($c->date_delete)) {
  832. foreach ($c->productOrder as $po) {
  833. if ($po->id_product == $idProduct) {
  834. if ($po->unit == 'piece') {
  835. $quantity += $po->quantity;
  836. } else {
  837. if (isset($po->product) && $po->product->weight > 0) {
  838. $quantity += ($po->quantity * Product::$unitsArray[$po->unit]['coefficient']) / $po->product->weight;
  839. }
  840. }
  841. }
  842. }
  843. }
  844. }
  845. }
  846. return $quantity;
  847. }
  848. /**
  849. * Recherche et initialise des commandes.
  850. *
  851. * @param array $params
  852. * @param array $conditions
  853. * @param string $orderby
  854. * @param integer $limit
  855. * @return array
  856. */
  857. public static function searchBy($params = [], $options = [])
  858. {
  859. $orders = parent::searchBy($params, $options);
  860. /*
  861. * Initialisation des commandes
  862. */
  863. if (is_array($orders)) {
  864. if (count($orders)) {
  865. foreach ($orders as $order) {
  866. if (is_a($order, 'common\models\Order')) {
  867. $order->init();
  868. }
  869. }
  870. return $orders;
  871. }
  872. } else {
  873. $order = $orders;
  874. if (is_a($order, 'common\models\Order')) {
  875. return $order->init();
  876. } // count
  877. else {
  878. return $order;
  879. }
  880. }
  881. return false;
  882. }
  883. /**
  884. * Retourne le nombre de produits commandés
  885. *
  886. * @return integer
  887. */
  888. public function countProducts()
  889. {
  890. $count = 0;
  891. if ($this->productOrder && is_array($this->productOrder)) {
  892. return count($this->productOrder);
  893. }
  894. return 0;
  895. }
  896. /**
  897. * Retourne un bloc html présentant une date.
  898. *
  899. * @return string
  900. */
  901. public function getBlockDate()
  902. {
  903. return '<div class="block-date">
  904. <div class="day">' . strftime('%A', strtotime($this->distribution->date)) . '</div>
  905. <div class="num">' . date('d', strtotime($this->distribution->date)) . '</div>
  906. <div class="month">' . strftime('%B', strtotime($this->distribution->date)) . '</div>
  907. </div>';
  908. }
  909. public function getUsername()
  910. {
  911. $username = '';
  912. if ($this->user) {
  913. $username = $this->user->getUsername();
  914. }
  915. if (strlen($this->username)) {
  916. $username = $this->username;
  917. }
  918. return $username;
  919. }
  920. public function initInvoicePrices($params = [])
  921. {
  922. foreach ($this->productOrder as $productOrder) {
  923. if ($productOrder->product) {
  924. $productOrder->invoice_price = $productOrder->product->getPrice([
  925. 'user' => isset($params['user']) ? $params['user'] : null,
  926. 'user_producer' => isset($params['user_producer']) ? $params['user_producer'] : null,
  927. 'point_sale' => isset($params['point_sale']) ? $params['point_sale'] : null,
  928. 'quantity' => $productOrder->quantity
  929. ]);
  930. $productOrder->save();
  931. }
  932. }
  933. }
  934. public function initReference()
  935. {
  936. $idProducer = GlobalParam::getCurrentProducerId();
  937. $producer = Producer::findOne($idProducer);
  938. if (!$this->reference && $producer->option_order_reference_type == Producer::ORDER_REFERENCE_TYPE_YEARLY) {
  939. $lastOrder = Order::find()->innerJoinWith('distribution', true)
  940. ->where(['>=', 'distribution.date', date('Y') . '-01-01'])
  941. ->andWhere([
  942. 'distribution.id_producer' => $producer->id
  943. ])
  944. ->andWhere(['not', ['order.reference' => null]])
  945. ->orderBy('order.reference DESC')
  946. ->one();
  947. if ($lastOrder && $lastOrder->reference && strlen($lastOrder->reference) > 0) {
  948. $pattern = '#A([0-9]+)C([0-9]+)#';
  949. preg_match($pattern, $lastOrder->reference, $matches, PREG_OFFSET_CAPTURE);
  950. $sizeNumReference = strlen($matches[2][0]);
  951. $numReference = ((int)$matches[2][0]) + 1;
  952. $numReference = str_pad($numReference, $sizeNumReference, '0', STR_PAD_LEFT);
  953. $this->reference = 'A' . $matches[1][0] . 'C' . $numReference;
  954. } else {
  955. $this->reference = 'A' . date('y') . 'C0001';
  956. }
  957. $this->save();
  958. }
  959. }
  960. public function getCommentReport()
  961. {
  962. $comment = '';
  963. $hasComment = false;
  964. if ($this->comment && strlen($this->comment) > 0) {
  965. $hasComment = true;
  966. $comment .= $this->comment;
  967. }
  968. if ($this->delivery_home && $this->delivery_address && strlen($this->delivery_address) > 0) {
  969. if ($hasComment) {
  970. $comment .= '<br /><br />';
  971. }
  972. $comment .= '<strong>Livraison à domicile :</strong><br />';
  973. $comment .= nl2br($this->delivery_address);
  974. }
  975. return $comment;
  976. }
  977. }