Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

6 роки тому
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  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($force = false)
  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. $force) {
  330. ProductOrder::deleteAll(['id_order' => $this->id]);
  331. return parent::delete();
  332. } // status 'delete'
  333. elseif (Producer::getConfig('option_behavior_cancel_order') == Producer::BEHAVIOR_DELETE_ORDER_STATUS) {
  334. $this->date_delete = date('Y-m-d H:i:s');
  335. return $this->save();
  336. }
  337. }
  338. /**
  339. * Changement de statut d'une commande
  340. *
  341. * @param $newStatus
  342. */
  343. public function changeOrderStatus($newStatus, $origin)
  344. {
  345. $orderStatusArray = GlobalParam::get('orderStatus');
  346. switch ($newStatus) {
  347. case 'new-order' :
  348. $this->addOrderStatusHistory($newStatus, $origin);
  349. $this->status = $newStatus;
  350. $this->save();
  351. break;
  352. case 'waiting-paiement-on-delivery':
  353. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  354. $this->addOrderStatusHistory($newStatus, $origin);
  355. $this->status = $newStatus;
  356. $this->save();
  357. }
  358. break;
  359. case 'waiting-paiement-by-credit':
  360. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  361. $this->addOrderStatusHistory($newStatus, $origin);
  362. $this->status = $newStatus;
  363. $this->save();
  364. }
  365. break;
  366. case 'paid-by-credit':
  367. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  368. $this->addOrderStatusHistory($newStatus, $origin);
  369. $this->status = $newStatus;
  370. $this->save();
  371. }
  372. break;
  373. case 'waiting-delevery' :
  374. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  375. $this->addOrderStatusHistory($newStatus, $origin);
  376. $this->status = $newStatus;
  377. $this->save();
  378. }
  379. break;
  380. case 'delivered':
  381. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  382. $this->addOrderStatusHistory($newStatus, $origin);
  383. $this->status = $newStatus;
  384. $this->save();
  385. }
  386. break;
  387. case 'refunded':
  388. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  389. $this->addOrderStatusHistory($newStatus, $origin);
  390. $this->status = $newStatus;
  391. $this->save();
  392. }
  393. break;
  394. case 'cancel':
  395. if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) {
  396. $this->addOrderStatusHistory($newStatus, $origin);
  397. $this->status = $newStatus;
  398. $this->save();
  399. }
  400. break;
  401. default:
  402. throw new NotFoundHttpException('Statut de commande inconnu.');
  403. break;
  404. }
  405. }
  406. public function addOrderStatusHistory($newStatus, $origin)
  407. {
  408. $orderStatusHistory = new OrderStatusHistory();
  409. $orderStatusHistory->id_user = User::getCurrentId();
  410. $orderStatusHistory->id_order = $this->id;
  411. $orderStatusHistory->status = $newStatus;
  412. $orderStatusHistory->origin = $origin;
  413. $orderStatusHistory->date = date('Y-m-d H:i:s');
  414. $orderStatusHistory->save();
  415. }
  416. /**
  417. * Retourne le montant de la commande (total, payé, restant, ou en surplus).
  418. *
  419. * @param boolean $format
  420. * @return float
  421. */
  422. public function getAmount($type = self::AMOUNT_TOTAL, $format = false)
  423. {
  424. $amount = $this->amount;
  425. if ($type == self::INVOICE_AMOUNT_TOTAL && $this->invoice_amount) {
  426. $amount = $this->invoice_amount;
  427. }
  428. return $this->_getAmountGeneric($type, $amount, $format);
  429. }
  430. public function getAmountWithTax($type = self::AMOUNT_TOTAL, $format = false)
  431. {
  432. $amount = $this->amount + $this->getTotalVat($type);
  433. if ($type == self::INVOICE_AMOUNT_TOTAL && $this->invoice_amount) {
  434. $amount = $this->invoice_amount + $this->getTotalVat($type);
  435. }
  436. return $this->_getAmountGeneric($type, $amount, $format);
  437. }
  438. protected function _getAmountGeneric($type, $amountOrder, $format)
  439. {
  440. switch ($type) {
  441. case self::AMOUNT_TOTAL :
  442. case self::INVOICE_AMOUNT_TOTAL :
  443. $amount = $amountOrder;
  444. break;
  445. case self::AMOUNT_PAID :
  446. $this->initPaidAmount();
  447. $amount = $this->paid_amount;
  448. break;
  449. case self::AMOUNT_REMAINING :
  450. $amount = $this->getAmountWithTax(self::AMOUNT_TOTAL)
  451. - $this->getAmountWithTax(self::AMOUNT_PAID);
  452. break;
  453. case self::AMOUNT_SURPLUS :
  454. $amount = $this->getAmountWithTax(self::AMOUNT_PAID)
  455. - $this->getAmountWithTax(self::AMOUNT_TOTAL);
  456. break;
  457. }
  458. if ($format) {
  459. return Price::format($amount);
  460. } else {
  461. return $amount;
  462. }
  463. }
  464. /**
  465. * Retourne les informations relatives à la commande au format JSON.
  466. *
  467. * @return string
  468. */
  469. public function getDataJson()
  470. {
  471. $order = Order::searchOne(['order.id' => $this->id]);
  472. $jsonOrder = [];
  473. if ($order) {
  474. $jsonOrder = [
  475. 'products' => [],
  476. 'amount' => $order->amount,
  477. 'str_amount' => $order->getAmountWithTax(self::AMOUNT_TOTAL, true),
  478. 'paid_amount' => $order->getAmount(self::AMOUNT_PAID),
  479. 'comment' => $order->comment,
  480. ];
  481. foreach ($order->productOrder as $productOrder) {
  482. $jsonOrder['products'][$productOrder->id_product] = $productOrder->quantity;
  483. }
  484. }
  485. return json_encode($jsonOrder);
  486. }
  487. /**
  488. * Enregistre un modèle de type CreditHistory.
  489. *
  490. * @param string $type
  491. * @param float $montant
  492. * @param integer $idProducer
  493. * @param integer $idUser
  494. * @param integer $idUserAction
  495. */
  496. public function saveCreditHistory($type, $amount, $idProducer, $idUser, $idUserAction)
  497. {
  498. $creditHistory = new CreditHistory;
  499. $creditHistory->id_user = $idUser;
  500. $creditHistory->id_order = $this->id;
  501. $creditHistory->amount = round($amount, 2);
  502. $creditHistory->type = $type;
  503. $creditHistory->id_producer = $idProducer;
  504. $creditHistory->id_user_action = $idUserAction;
  505. $creditHistory->populateRelation('order', $this);
  506. $creditHistory->populateRelation('user', User::find()->where(['id' => $this->id_user])->one());
  507. $creditHistory->save();
  508. }
  509. /**
  510. * Ajuste le crédit pour que la commande soit payée.
  511. *
  512. * @return boolean
  513. */
  514. public function processCredit()
  515. {
  516. if ($this->id_user) {
  517. $paymentStatus = $this->getPaymentStatus();
  518. if ($paymentStatus == self::PAYMENT_PAID) {
  519. return true;
  520. } elseif ($paymentStatus == self::PAYMENT_SURPLUS) {
  521. $type = CreditHistory::TYPE_REFUND;
  522. $amount = $this->getAmount(self::AMOUNT_SURPLUS);
  523. } elseif ($paymentStatus == self::PAYMENT_UNPAID) {
  524. $type = CreditHistory::TYPE_PAYMENT;
  525. $amount = $this->getAmount(self::AMOUNT_REMAINING);
  526. }
  527. $this->saveCreditHistory(
  528. $type,
  529. $amount,
  530. GlobalParam::getCurrentProducerId(),
  531. $this->id_user,
  532. User::getCurrentId()
  533. );
  534. }
  535. }
  536. public function setTillerSynchronization()
  537. {
  538. $order = Order::searchOne(['id' => $this->id]);
  539. $paymentStatus = $order->getPaymentStatus();
  540. if ($paymentStatus == self::PAYMENT_PAID) {
  541. $order->tiller_synchronization = 1;
  542. } else {
  543. $order->tiller_synchronization = 0;
  544. }
  545. $order->save();
  546. return $order;
  547. }
  548. /**
  549. * Retourne le statut de paiement de la commande (payée, surplus, ou impayée).
  550. *
  551. * @return string
  552. */
  553. public function getPaymentStatus()
  554. {
  555. // payé
  556. if ($this->getAmountWithtax() - $this->getAmount(self::AMOUNT_PAID) < 0.01 &&
  557. $this->getAmountWithtax() - $this->getAmount(self::AMOUNT_PAID) > -0.01) {
  558. return self::PAYMENT_PAID;
  559. } // à rembourser
  560. elseif ($this->getAmountWithtax() - $this->getAmount(self::AMOUNT_PAID) <= -0.01) {
  561. return self::PAYMENT_SURPLUS;
  562. } // reste à payer
  563. elseif ($this->getAmountWithtax() - $this->getAmount(self::AMOUNT_PAID) >= 0.01) {
  564. return self::PAYMENT_UNPAID;
  565. }
  566. }
  567. /**
  568. * Retourne le résumé du panier au format HTML.
  569. *
  570. * @return string
  571. */
  572. public function getCartSummary($htmlFormat = true)
  573. {
  574. if (!isset($this->productOrder)) {
  575. $this->productOrder = productOrder::find()->where(['id_order' => $this->id])->all();
  576. }
  577. $html = '';
  578. $count = count($this->productOrder);
  579. $i = 0;
  580. foreach ($this->productOrder as $p) {
  581. if (isset($p->product)) {
  582. $html .= Html::encode($p->product->name) . ' (' . $p->quantity . '&nbsp;' . Product::strUnit(
  583. $p->unit,
  584. 'wording_short',
  585. true
  586. ) . ')';
  587. if (++$i != $count) {
  588. if ($htmlFormat) {
  589. $html .= '<br />';
  590. } else {
  591. $html .= "\n";
  592. }
  593. }
  594. }
  595. }
  596. return $html;
  597. }
  598. /**
  599. * Retourne le résumé du point de vente lié à la commande au format HTML.
  600. *
  601. * @return string
  602. */
  603. public function getPointSaleSummary()
  604. {
  605. $html = '';
  606. if (isset($this->pointSale)) {
  607. $html .= '<span class="name-point-sale">' .
  608. Html::encode($this->pointSale->name) .
  609. '</span>' .
  610. '<br /><span class="locality">'
  611. . Html::encode($this->pointSale->locality)
  612. . '</span>';
  613. if (strlen($this->comment_point_sale)) {
  614. $html .= '<div class="comment"><span>'
  615. . Html::encode($this->comment_point_sale)
  616. . '</span></div>';
  617. }
  618. } else {
  619. $html .= 'Point de vente supprimé';
  620. }
  621. return $html;
  622. }
  623. /**
  624. * Retourne le résumé du paiement (montant, statut).
  625. *
  626. * @return string
  627. */
  628. public function getAmountSummary()
  629. {
  630. $html = '';
  631. $creditActive = Producer::getConfig('credit');
  632. $html .= $this->getAmountWithTax(self::AMOUNT_TOTAL, true);
  633. if ($creditActive) {
  634. $html .= '<br />';
  635. if ($this->paid_amount) {
  636. if ($this->getPaymentStatus() == Order::PAYMENT_PAID) {
  637. $html .= '<span class="label label-success">Payée</span>';
  638. } elseif ($this->getPaymentStatus() == Order::PAYMENT_UNPAID) {
  639. $html .= '<span class="label label-danger">Non payée</span><br />
  640. Reste <strong>' . $this->getAmount(
  641. Order::AMOUNT_REMAINING,
  642. true
  643. ) . '</strong> à payer';
  644. } elseif ($this->getPaymentStatus() == Order::PAYMENT_SURPLUS) {
  645. $html .= '<span class="label label-success">Payée</span>';
  646. }
  647. } else {
  648. $html .= '<span class="label label-default">Non réglé</span>';
  649. }
  650. }
  651. return $html;
  652. }
  653. /**
  654. * Retourne une chaine de caractère décrivant l'utilisateur lié à la commande.
  655. *
  656. * @return string
  657. */
  658. public function getStrUser()
  659. {
  660. if (isset($this->user)) {
  661. if (isset($this->user->name_legal_person) && strlen($this->user->name_legal_person)) {
  662. return Html::encode($this->user->name_legal_person);
  663. } else {
  664. $strUser = $this->user->lastname;
  665. if($this->user->lastname && $this->user->name) {
  666. $strUser .= ' '.$this->user->name;
  667. }
  668. return Html::encode($strUser);
  669. }
  670. } elseif (strlen($this->username)) {
  671. return Html::encode($this->username);
  672. } else {
  673. return 'Client introuvable';
  674. }
  675. }
  676. /**
  677. * Retourne l'état de la commande (livrée, modifiable ou en préparation)
  678. *
  679. * @return string
  680. */
  681. public function getState()
  682. {
  683. $orderDate = strtotime($this->distribution->date);
  684. $today = strtotime(date('Y-m-d'));
  685. $todayHour = date('G');
  686. $dayDistribution = strtolower(date('l', strtotime($this->distribution->date)));
  687. $orderDelay = Producer::getConfig(
  688. 'order_delay',
  689. $this->distribution->id_producer
  690. );
  691. $orderDelaySpecific = Producer::getConfig(
  692. 'order_delay_' . $dayDistribution,
  693. $this->distribution->id_producer
  694. );
  695. if ($orderDelaySpecific) {
  696. $orderDelay = $orderDelaySpecific;
  697. }
  698. $orderDeadline = Producer::getConfig(
  699. 'order_deadline',
  700. $this->distribution->id_producer
  701. );
  702. $orderDeadlineSpecific = Producer::getConfig(
  703. 'order_deadline_' . $dayDistribution,
  704. $this->distribution->id_producer
  705. );
  706. if ($orderDeadlineSpecific) {
  707. $orderDeadline = $orderDeadlineSpecific;
  708. }
  709. $nbDays = (int)round((($orderDate - $today) / (24 * 60 * 60)));
  710. if ($nbDays <= 0) {
  711. return self::STATE_DELIVERED;
  712. } elseif ($nbDays >= $orderDelay &&
  713. ($nbDays != $orderDelay ||
  714. ($nbDays == $orderDelay && $todayHour < $orderDeadline))) {
  715. return self::STATE_OPEN;
  716. }
  717. return self::STATE_PREPARATION;
  718. }
  719. /**
  720. * Retourne l'origine de la commande (client, automatique ou admin) sous forme
  721. * texte ou HTML.
  722. *
  723. * @param boolean $with_label
  724. * @return string
  725. */
  726. public function getStrOrigin($withLabel = false)
  727. {
  728. $classLabel = '';
  729. $str = '';
  730. if ($this->origin == self::ORIGIN_USER) {
  731. $classLabel = 'success';
  732. $str = 'Client';
  733. } elseif ($this->origin == self::ORIGIN_AUTO) {
  734. $classLabel = 'default';
  735. $str = 'Auto';
  736. } elseif ($this->origin == self::ORIGIN_ADMIN) {
  737. $classLabel = 'warning';
  738. $str = 'Vous';
  739. }
  740. if ($withLabel) {
  741. return '<span class="label label-' . $classLabel . '">'
  742. . $str . '</span>';
  743. } else {
  744. return $str;
  745. }
  746. }
  747. /**
  748. * Retourne l'historique de la commande (ajoutée, modifiée, supprimée) au
  749. * format HTML.
  750. *
  751. * @return string
  752. */
  753. public function getStrHistory()
  754. {
  755. $arr = [
  756. 'class' => 'create',
  757. 'glyphicon' => 'plus',
  758. 'str' => 'Ajoutée',
  759. 'date' => $this->date
  760. ];
  761. if (!is_null($this->date_update)) {
  762. $arr = [
  763. 'class' => 'update',
  764. 'glyphicon' => 'pencil',
  765. 'str' => 'Modifiée',
  766. 'date' => $this->date_update
  767. ];
  768. }
  769. if (!is_null($this->date_delete)) {
  770. $arr = [
  771. 'class' => 'delete',
  772. 'glyphicon' => 'remove',
  773. 'str' => 'Annulée',
  774. 'date' => $this->date_delete
  775. ];
  776. }
  777. $html = '<div class="small"><span class="' . $arr['class'] . '">'
  778. . '<span class="glyphicon glyphicon-' . $arr['glyphicon'] . '"></span> '
  779. . $arr['str'] . '</span> le <strong>'
  780. . date('d/m/Y à G\hi', strtotime($arr['date'])) . '</strong></div>';
  781. return $html;
  782. }
  783. /**
  784. * Retourne une classe identifiant l'historique de la commande (ajoutée,
  785. * modifiée, supprimée).
  786. *
  787. * @return string
  788. */
  789. public function getClassHistory()
  790. {
  791. if (!is_null($this->date_delete)) {
  792. return 'commande-delete';
  793. }
  794. if (!is_null($this->date_update)) {
  795. return 'commande-update';
  796. }
  797. return 'commande-create';
  798. }
  799. /**
  800. * Retourne la quantité d'un produit donné de plusieurs commandes.
  801. *
  802. * @param integer $idProduct
  803. * @param array $orders
  804. * @param boolean $ignoreCancel
  805. *
  806. * @return integer
  807. */
  808. public static function getProductQuantity($idProduct, $orders, $ignoreCancel = false, $unit = null)
  809. {
  810. $quantity = 0;
  811. if (isset($orders) && is_array($orders) && count($orders)) {
  812. foreach ($orders as $c) {
  813. if (is_null($c->date_delete) || $ignoreCancel) {
  814. foreach ($c->productOrder as $po) {
  815. if ($po->id_product == $idProduct &&
  816. ((is_null($unit) && $po->product->unit == $po->unit) || (!is_null($unit) && strlen(
  817. $unit
  818. ) && $po->unit == $unit))) {
  819. $quantity += $po->quantity;
  820. }
  821. }
  822. }
  823. }
  824. }
  825. return $quantity;
  826. }
  827. public static function getProductQuantityPieces($idProduct, $orders)
  828. {
  829. $quantity = 0;
  830. if (isset($orders) && is_array($orders) && count($orders)) {
  831. foreach ($orders as $c) {
  832. if (is_null($c->date_delete)) {
  833. foreach ($c->productOrder as $po) {
  834. if ($po->id_product == $idProduct) {
  835. if ($po->unit == 'piece') {
  836. $quantity += $po->quantity;
  837. } else {
  838. if (isset($po->product) && $po->product->weight > 0) {
  839. $quantity += ($po->quantity * Product::$unitsArray[$po->unit]['coefficient']) / $po->product->weight;
  840. }
  841. }
  842. }
  843. }
  844. }
  845. }
  846. }
  847. return $quantity;
  848. }
  849. /**
  850. * Recherche et initialise des commandes.
  851. *
  852. * @param array $params
  853. * @param array $conditions
  854. * @param string $orderby
  855. * @param integer $limit
  856. * @return array
  857. */
  858. public static function searchBy($params = [], $options = [])
  859. {
  860. $orders = parent::searchBy($params, $options);
  861. /*
  862. * Initialisation des commandes
  863. */
  864. if (is_array($orders)) {
  865. if (count($orders)) {
  866. foreach ($orders as $order) {
  867. if (is_a($order, 'common\models\Order')) {
  868. $order->init();
  869. }
  870. }
  871. return $orders;
  872. }
  873. } else {
  874. $order = $orders;
  875. if (is_a($order, 'common\models\Order')) {
  876. return $order->init();
  877. } // count
  878. else {
  879. return $order;
  880. }
  881. }
  882. return false;
  883. }
  884. /**
  885. * Retourne le nombre de produits commandés
  886. *
  887. * @return integer
  888. */
  889. public function countProducts()
  890. {
  891. $count = 0;
  892. if ($this->productOrder && is_array($this->productOrder)) {
  893. return count($this->productOrder);
  894. }
  895. return 0;
  896. }
  897. /**
  898. * Retourne un bloc html présentant une date.
  899. *
  900. * @return string
  901. */
  902. public function getBlockDate()
  903. {
  904. return '<div class="block-date">
  905. <div class="day">' . strftime('%A', strtotime($this->distribution->date)) . '</div>
  906. <div class="num">' . date('d', strtotime($this->distribution->date)) . '</div>
  907. <div class="month">' . strftime('%B', strtotime($this->distribution->date)) . '</div>
  908. </div>';
  909. }
  910. public function getUsername()
  911. {
  912. $username = '';
  913. if ($this->user) {
  914. $username = $this->user->getUsername();
  915. }
  916. if (strlen($this->username)) {
  917. $username = $this->username;
  918. }
  919. return $username;
  920. }
  921. public function initInvoicePrices($params = [])
  922. {
  923. foreach ($this->productOrder as $productOrder) {
  924. if ($productOrder->product) {
  925. $productOrder->invoice_price = $productOrder->product->getPrice([
  926. 'user' => isset($params['user']) ? $params['user'] : null,
  927. 'user_producer' => isset($params['user_producer']) ? $params['user_producer'] : null,
  928. 'point_sale' => isset($params['point_sale']) ? $params['point_sale'] : null,
  929. 'quantity' => $productOrder->quantity
  930. ]);
  931. $productOrder->save();
  932. }
  933. }
  934. }
  935. public function initReference()
  936. {
  937. $idProducer = GlobalParam::getCurrentProducerId();
  938. $producer = Producer::findOne($idProducer);
  939. if (!$this->reference && $producer->option_order_reference_type == Producer::ORDER_REFERENCE_TYPE_YEARLY) {
  940. $lastOrder = Order::find()->innerJoinWith('distribution', true)
  941. ->where(['>=', 'distribution.date', date('Y') . '-01-01'])
  942. ->andWhere([
  943. 'distribution.id_producer' => $producer->id
  944. ])
  945. ->andWhere(['not', ['order.reference' => null]])
  946. ->orderBy('order.reference DESC')
  947. ->one();
  948. if ($lastOrder && $lastOrder->reference && strlen($lastOrder->reference) > 0) {
  949. $pattern = '#A([0-9]+)C([0-9]+)#';
  950. preg_match($pattern, $lastOrder->reference, $matches, PREG_OFFSET_CAPTURE);
  951. $sizeNumReference = strlen($matches[2][0]);
  952. $numReference = ((int)$matches[2][0]) + 1;
  953. $numReference = str_pad($numReference, $sizeNumReference, '0', STR_PAD_LEFT);
  954. $this->reference = 'A' . $matches[1][0] . 'C' . $numReference;
  955. } else {
  956. $this->reference = 'A' . date('y') . 'C0001';
  957. }
  958. $this->save();
  959. }
  960. }
  961. public function getCommentReport()
  962. {
  963. $comment = '';
  964. $hasComment = false;
  965. if ($this->comment && strlen($this->comment) > 0) {
  966. $hasComment = true;
  967. $comment .= $this->comment;
  968. }
  969. if ($this->delivery_home && $this->delivery_address && strlen($this->delivery_address) > 0) {
  970. if ($hasComment) {
  971. $comment .= '<br /><br />';
  972. }
  973. $comment .= '<strong>Livraison à domicile :</strong><br />';
  974. $comment .= nl2br($this->delivery_address);
  975. }
  976. return $comment;
  977. }
  978. public function isLinkedToValidDocument()
  979. {
  980. return ($this->deliveryNote && $this->deliveryNote->isStatusValid())
  981. || ($this->quotation && $this->quotation->isStatusValid())
  982. || ($this->invoice && $this->invoice->isStatusValid());
  983. }
  984. public function isCreditAutoPayment()
  985. {
  986. $pointSale = PointSale::findOne($this->id_point_sale);
  987. $distribution = Distribution::findOne($this->id_distribution);
  988. $creditFunctioning = $pointSale->getCreditFunctioning();
  989. if ($this->id_user && Producer::getConfig('credit') && $pointSale->credit) {
  990. if ($creditFunctioning == Producer::CREDIT_FUNCTIONING_OPTIONAL) {
  991. return 0;
  992. } elseif ($creditFunctioning == Producer::CREDIT_FUNCTIONING_MANDATORY) {
  993. return 1;
  994. } elseif ($creditFunctioning == Producer::CREDIT_FUNCTIONING_USER) {
  995. $userProducer = UserProducer::searchOne([
  996. 'id_user' => $this->id_user,
  997. 'id_producer' => $distribution->id_producer
  998. ]);
  999. if ($userProducer) {
  1000. return $userProducer->credit_active;
  1001. }
  1002. }
  1003. }
  1004. return 0;
  1005. }
  1006. }