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

590 lines
22KB

  1. <?php
  2. namespace domain\Order\Order;
  3. use common\helpers\GlobalParam;
  4. use common\helpers\MeanPayment;
  5. use common\helpers\Price;
  6. use domain\Config\TaxRate\TaxRate;
  7. use domain\Distribution\Distribution\Distribution;
  8. use domain\Distribution\Distribution\DistributionRepository;
  9. use domain\Distribution\Distribution\DistributionSolver;
  10. use domain\Document\DeliveryNote\DeliveryNote;
  11. use domain\Document\DeliveryNote\DeliveryNoteBuilder;
  12. use domain\Document\Document\Document;
  13. use domain\Document\Document\DocumentInterface;
  14. use domain\Document\Document\DocumentSolver;
  15. use domain\Document\Invoice\Invoice;
  16. use domain\Order\Order\Event\OrderDeleteEvent;
  17. use domain\Order\OrderStatus\OrderStatus;
  18. use domain\Order\OrderStatus\OrderStatusRepository;
  19. use domain\Order\ProductOrder\ProductOrder;
  20. use domain\Order\ProductOrder\ProductOrderBuilder;
  21. use domain\Order\ProductOrder\ProductOrderSolver;
  22. use domain\Payment\Payment;
  23. use domain\Payment\PaymentBuilder;
  24. use domain\Payment\PaymentRepository;
  25. use domain\PointSale\PointSale\PointSale;
  26. use domain\PointSale\PointSale\PointSaleBuilder;
  27. use domain\PointSale\PointSale\PointSaleRepository;
  28. use domain\PointSale\UserPointSale\UserPointSaleRepository;
  29. use domain\Producer\Producer\Producer;
  30. use domain\Producer\Producer\ProducerRepository;
  31. use domain\Producer\Producer\ProducerSolver;
  32. use domain\Product\Product\Product;
  33. use domain\Product\Product\ProductRepository;
  34. use domain\Product\Product\ProductSolver;
  35. use domain\Subscription\Subscription\Subscription;
  36. use domain\Subscription\Subscription\SubscriptionBuilder;
  37. use domain\Subscription\Subscription\SubscriptionRepository;
  38. use domain\Subscription\Subscription\SubscriptionSolver;
  39. use domain\User\User\UserRepository;
  40. use domain\User\User\UserSolver;
  41. use domain\User\UserProducer\UserProducerRepository;
  42. use domain\_\AbstractBuilder;
  43. use yii\web\NotFoundHttpException;
  44. class OrderBuilder extends AbstractBuilder
  45. {
  46. protected UserSolver $userSolver;
  47. protected OrderSolver $orderSolver;
  48. protected PaymentRepository $paymentRepository;
  49. protected ProducerRepository $producerRepository;
  50. protected PaymentBuilder $paymentBuilder;
  51. protected ProductOrderBuilder $productOrderBuilder;
  52. protected OrderRepository $orderRepository;
  53. protected DistributionRepository $distributionRepository;
  54. protected SubscriptionBuilder $subscriptionBuilder;
  55. protected PointSaleRepository $pointSaleRepository;
  56. protected UserProducerRepository $userProducerRepository;
  57. protected UserPointSaleRepository $userPointSaleRepository;
  58. protected PointSaleBuilder $pointSaleBuilder;
  59. protected ProductOrderSolver $productOrderSolver;
  60. protected SubscriptionRepository $subscriptionRepository;
  61. protected SubscriptionSolver $subscriptionSolver;
  62. protected ProductSolver $productSolver;
  63. protected DistributionSolver $distributionSolver;
  64. protected UserRepository $userRepository;
  65. protected DeliveryNoteBuilder $deliveryNoteBuilder;
  66. protected DocumentSolver $documentSolver;
  67. protected ProducerSolver $producerSolver;
  68. protected OrderStatusRepository $orderStatusRepository;
  69. protected ProductRepository $productRepository;
  70. public function loadDependencies(): void
  71. {
  72. $this->userSolver = $this->loadService(UserSolver::class);
  73. $this->orderSolver = $this->loadService(OrderSolver::class);
  74. $this->paymentRepository = $this->loadService(PaymentRepository::class);
  75. $this->producerRepository = $this->loadService(ProducerRepository::class);
  76. $this->paymentBuilder = $this->loadService(PaymentBuilder::class);
  77. $this->productOrderBuilder = $this->loadService(ProductOrderBuilder::class);
  78. $this->orderRepository = $this->loadService(OrderRepository::class);
  79. $this->distributionRepository = $this->loadService(DistributionRepository::class);
  80. $this->subscriptionBuilder = $this->loadService(SubscriptionBuilder::class);
  81. $this->pointSaleRepository = $this->loadService(PointSaleRepository::class);
  82. $this->userProducerRepository = $this->loadService(UserProducerRepository::class);
  83. $this->userPointSaleRepository = $this->loadService(UserPointSaleRepository::class);
  84. $this->pointSaleBuilder = $this->loadService(PointSaleBuilder::class);
  85. $this->productOrderSolver = $this->loadService(ProductOrderSolver::class);
  86. $this->subscriptionRepository = $this->loadService(SubscriptionRepository::class);
  87. $this->subscriptionSolver = $this->loadService(SubscriptionSolver::class);
  88. $this->productSolver = $this->loadService(ProductSolver::class);
  89. $this->distributionSolver = $this->loadService(DistributionSolver::class);
  90. $this->userRepository = $this->loadService(UserRepository::class);
  91. $this->deliveryNoteBuilder = $this->loadService(DeliveryNoteBuilder::class);
  92. $this->documentSolver = $this->loadService(DocumentSolver::class);
  93. $this->producerSolver = $this->loadService(ProducerSolver::class);
  94. $this->orderStatusRepository = $this->loadService(OrderStatusRepository::class);
  95. $this->productRepository = $this->loadService(ProductRepository::class);
  96. }
  97. public function instanciateOrder(Distribution $distribution): Order
  98. {
  99. $order = new Order();
  100. $order->populateDistribution($distribution);
  101. $order->date = date('Y-m-d H:i:s');
  102. $order->id_user = 0;
  103. return $order;
  104. }
  105. public function createOrder(Distribution $distribution): Order
  106. {
  107. $order = $this->instanciateOrder($distribution);
  108. $this->createUserPointSale($order);
  109. $this->initOrderCommentPointSale($order);
  110. $this->generateOrderReference($order);
  111. $order->save();
  112. return $order;
  113. }
  114. public function instanciateOrderFromProductOrdersArray(array $productOrdersArray, Order $order = null): Order
  115. {
  116. if(!$order) {
  117. $order = new Order();
  118. $this->initOrderStatus($order);
  119. }
  120. $productOrderObjectArray = [];
  121. foreach($productOrdersArray as $idProduct => $productOrder) {
  122. if($productOrder['quantity']) {
  123. $product = $this->productRepository->findOneProductById($idProduct);
  124. $productOrderObject = $this->productOrderBuilder->instanciateProductOrder(
  125. $order,
  126. $product,
  127. $productOrder['quantity'],
  128. 0
  129. );
  130. $productOrderObjectArray[] = $productOrderObject;
  131. }
  132. }
  133. $order->populateRelation('productOrder', $productOrderObjectArray);
  134. return $order;
  135. }
  136. public function addOrUpdateOrIgnoreOrderFromArray(Order $orderOverride, array $ordersArray, bool $ignoreOrderCurrent = false): array
  137. {
  138. $override = false;
  139. foreach($ordersArray as $key => $order) {
  140. if($order->id == $orderOverride->id) {
  141. if($ignoreOrderCurrent) {
  142. unset($ordersArray[$key]);
  143. }
  144. else {
  145. $ordersArray[$key] = $orderOverride;
  146. $override = true;
  147. }
  148. }
  149. }
  150. if(!$override && !$ignoreOrderCurrent) {
  151. $ordersArray[] = $orderOverride;
  152. }
  153. return $ordersArray;
  154. }
  155. public function deleteProductOrderQuantity(Order $order, Product $product, float $quantity)
  156. {
  157. foreach($order->productOrder as $productOrder) {
  158. if($productOrder->id_product == $product->id) {
  159. $productOrder->quantity -= $quantity;
  160. }
  161. }
  162. }
  163. public function initDateUpdate(Order $order)
  164. {
  165. $order->date_update = date('Y-m-d H:i:s');;
  166. }
  167. public function addProductOrdersFromSubscription(Order $order, Subscription $subscription): bool
  168. {
  169. $productsAdd = false;
  170. $user = $subscription->user ?? null;
  171. foreach ($subscription->productSubscription as $productSubscription) {
  172. $this->productOrderBuilder->createProductOrder(
  173. $order,
  174. $productSubscription->product,
  175. $productSubscription->quantity,
  176. (float) $this->productSolver->getPrice($productSubscription->product, [
  177. 'user' => $user,
  178. 'point_sale' => $subscription->pointSale,
  179. 'quantity' => $productSubscription->quantity
  180. ])
  181. );
  182. $productsAdd = true;
  183. }
  184. return $productsAdd;
  185. }
  186. public function updateOrderFromSubscription(Order $order, Subscription $subscription): void
  187. {
  188. $this->initOrderBaseFromSubscription($order, $subscription);
  189. $this->initOrderAutoPaymentFromSubscription($order, $subscription);
  190. $this->addProductOrdersFromSubscription($order, $subscription);
  191. $this->update($order);
  192. }
  193. public function initOrderBaseFromSubscription(Order $order, Subscription $subscription): void
  194. {
  195. $order->origin = Order::ORIGIN_AUTO;
  196. $order->populatePointSale($subscription->pointSale);
  197. $order->populateSubscription($subscription);
  198. if (strlen($subscription->comment)) {
  199. $order->comment = $subscription->comment;
  200. }
  201. if (strlen($subscription->username)) {
  202. $order->username = $subscription->username;
  203. $order->id_user = 0;
  204. } else {
  205. $order->populateUser($subscription->user);
  206. }
  207. }
  208. public function initOrderAutoPaymentFromSubscription(Order $order, Subscription $subscription): void
  209. {
  210. $pointSale = $subscription->pointSale;
  211. if ($pointSale) {
  212. $creditFunctioning = $this->producerRepository->getPointSaleCreditFunctioning($pointSale);
  213. $order->auto_payment = 0;
  214. if ($subscription->auto_payment == Subscription::AUTO_PAYMENT_DEDUCTED) {
  215. if ($order->id_user && $this->producerSolver->getConfig('credit') && $pointSale->payment_method_credit) {
  216. if ($creditFunctioning == Producer::CREDIT_FUNCTIONING_OPTIONAL) {
  217. $order->auto_payment = 0;
  218. } elseif ($creditFunctioning == Producer::CREDIT_FUNCTIONING_MANDATORY) {
  219. $order->auto_payment = 1;
  220. } elseif ($creditFunctioning == Producer::CREDIT_FUNCTIONING_USER) {
  221. $userProducer = $this->userProducerRepository->findOneUserProducer($order->user);
  222. if ($userProducer) {
  223. $order->auto_payment = $userProducer->credit_active;
  224. }
  225. }
  226. }
  227. } elseif ($subscription->auto_payment == Subscription::AUTO_PAYMENT_YES) {
  228. $order->auto_payment = 1;
  229. } elseif ($subscription->auto_payment == Subscription::AUTO_PAYMENT_NO) {
  230. $order->auto_payment = 0;
  231. }
  232. }
  233. $order->tiller_synchronization = $order->auto_payment;
  234. }
  235. public function createUserPointSale(Order $order): void
  236. {
  237. if ($order->user) {
  238. $this->pointSaleBuilder->addUser($order->user, $order->pointSale);
  239. }
  240. }
  241. public function initOrderCommentPointSale(Order $order): void
  242. {
  243. if ($order->user && $order->pointSale) {
  244. $userPointSale = $this->userPointSaleRepository->findOneUserPointSale($order->user, $order->pointSale);
  245. if ($userPointSale && strlen($userPointSale->comment)) {
  246. $order->comment_point_sale = $userPointSale->comment;
  247. }
  248. }
  249. }
  250. /**
  251. * Initialise le montant total, le montant déjà payé et le poids de la commande.
  252. */
  253. public function initOrder(Order $order): void
  254. {
  255. $taxCalculationMethod = $this->getProducerContext()->option_tax_calculation_method;
  256. $this->initOrderAmount($order, $taxCalculationMethod);
  257. $this->initOrderPaidAmount($order);
  258. $this->initOrderStatus($order);
  259. }
  260. public function initOrderStatus(Order $order)
  261. {
  262. $orderStatusAlias = $order->getOrderStatusAlias() ?: OrderStatus::ALIAS_ORDERED;
  263. $orderStatus = $this->orderStatusRepository->getOrderStatusByAlias($orderStatusAlias);
  264. $order->setOrderStatus($orderStatus);
  265. }
  266. /**
  267. * Initialise le montant de la commande.
  268. */
  269. public function initOrderAmount(Order $order, string $taxCalculationMethod = Document::TAX_CALCULATION_METHOD_DEFAULT): void
  270. {
  271. $order->amount = 0;
  272. $order->amount_with_tax = 0;
  273. $order->amount_vat = [];
  274. $order->invoice_amount = 0;
  275. $order->invoice_amount_with_tax = 0;
  276. $order->invoice_amount_vat = [];
  277. $order->weight = 0;
  278. if (isset($order->productOrder)) {
  279. foreach ($order->productOrder as $productOrder) {
  280. $this->addProductOrderAmount($order, Order::AMOUNT_TOTAL, $productOrder, $taxCalculationMethod);
  281. $this->addProductOrderAmount($order, Order::INVOICE_AMOUNT_TOTAL, $productOrder, $taxCalculationMethod);
  282. $this->addProductOrderWeight($order, $productOrder);
  283. }
  284. }
  285. }
  286. public function addProductOrderWeight(Order $order, ProductOrder $productOrder): void
  287. {
  288. if ($productOrder->unit == 'piece') {
  289. if (isset($productOrder->product)) {
  290. $order->weight += ($productOrder->quantity * $productOrder->product->weight) / 1000;
  291. }
  292. } else {
  293. $order->weight += $productOrder->quantity;
  294. }
  295. }
  296. public function addProductOrderAmount(Order $order, string $typeTotal, ProductOrder $productOrder, string $taxCalculationMethod): void
  297. {
  298. $fieldNameAmount = $this->orderSolver->getFieldNameAmount($typeTotal);
  299. $fieldNameAmountWithTax = $this->orderSolver->getFieldNameAmount($typeTotal, 'with_tax');
  300. $price = $this->productOrderSolver->getPriceByTypeTotal($productOrder, $typeTotal);
  301. $order->$fieldNameAmount += $price * $productOrder->quantity;
  302. $order->$fieldNameAmountWithTax += Price::getPriceWithTax(
  303. $price,
  304. $productOrder->taxRate->value,
  305. $taxCalculationMethod
  306. ) * $productOrder->quantity;
  307. $this->addProductOrderVat($order, $typeTotal, $price * $productOrder->quantity, $productOrder->taxRate, $taxCalculationMethod);
  308. }
  309. public function addProductOrderVat(
  310. Order $order,
  311. string $typeTotal,
  312. float $priceTotalWithoutTax,
  313. TaxRate $taxRate,
  314. string $taxCalculationMethod): void
  315. {
  316. $fieldName = $this->orderSolver->getFieldNameAmount($typeTotal, 'vat');
  317. if (!isset($order->$fieldName[$taxRate->id])) {
  318. $order->$fieldName[$taxRate->id] = 0;
  319. }
  320. $order->$fieldName[$taxRate->id] += Price::getVat($priceTotalWithoutTax, $taxRate->value, $taxCalculationMethod);
  321. }
  322. /**
  323. * Initialise le montant payé de la commande et le retourne.
  324. */
  325. public function initOrderPaidAmount(Order $order): void
  326. {
  327. $history = $order->payment;
  328. $order->paid_amount = 0;
  329. if ($history && count($history)) {
  330. foreach ($order->payment as $ch) {
  331. if ($ch->type == Payment::TYPE_PAYMENT) {
  332. $order->paid_amount += $ch->amount;
  333. } elseif ($ch->type == Payment::TYPE_REFUND) {
  334. $order->paid_amount -= $ch->amount;
  335. }
  336. }
  337. }
  338. }
  339. /**
  340. * Ajuste le crédit pour que la commande soit payée.
  341. */
  342. public function processCredit(Order $order): void
  343. {
  344. if ($order->id_user) {
  345. $paymentStatus = $this->orderSolver->getPaymentStatus($order);
  346. if ($paymentStatus == Order::PAYMENT_PAID) {
  347. return;
  348. } elseif ($paymentStatus == Order::PAYMENT_SURPLUS) {
  349. $type = Payment::TYPE_REFUND;
  350. $amount = $this->orderSolver->getOrderAmount($order, Order::AMOUNT_SURPLUS);
  351. } elseif ($paymentStatus == Order::PAYMENT_UNPAID) {
  352. $type = Payment::TYPE_PAYMENT;
  353. $amount = $this->orderSolver->getOrderAmount($order, Order::AMOUNT_REMAINING);
  354. }
  355. $this->paymentBuilder->createPayment(
  356. $type,
  357. $amount,
  358. GlobalParam::getCurrentProducer(),
  359. $order->user,
  360. $this->userSolver->getCurrent(),
  361. MeanPayment::CREDIT,
  362. null,
  363. null,
  364. $order
  365. );
  366. }
  367. }
  368. public function updateOrderTillerSynchronization(Order $order, int $synchroTiller = null): void
  369. {
  370. if (!is_null($synchroTiller)) {
  371. $order->tiller_synchronization = $synchroTiller;
  372. } else {
  373. $amountPaid = $this->orderSolver->getOrderAmountPaidByCredit($order);
  374. if ($amountPaid >= 0.01) {
  375. $order->tiller_synchronization = 1;
  376. } else {
  377. $order->tiller_synchronization = 0;
  378. }
  379. }
  380. $this->saveUpdate($order);
  381. }
  382. // initReference
  383. // generateReference
  384. public function generateOrderReference(Order $order): void
  385. {
  386. $distribution = $this->distributionRepository->findOneDistributionById($order->id_distribution);
  387. $producer = $this->producerRepository->findOneProducerById($distribution->id_producer);
  388. if (!$order->reference && $producer->option_order_reference_type == Producer::ORDER_REFERENCE_TYPE_YEARLY) {
  389. $lastOrder = $this->orderRepository->findOneOrderLastOfYear($producer);
  390. if ($lastOrder && $lastOrder->reference && strlen($lastOrder->reference) > 0) {
  391. $pattern = '#A([0-9]+)C([0-9]+)#';
  392. preg_match($pattern, $lastOrder->reference, $matches, PREG_OFFSET_CAPTURE);
  393. $sizeNumReference = strlen($matches[2][0]);
  394. $numReference = ((int)$matches[2][0]) + 1;
  395. $numReference = str_pad($numReference, $sizeNumReference, '0', STR_PAD_LEFT);
  396. $order->reference = 'A' . $matches[1][0] . 'C' . $numReference;
  397. } else {
  398. $order->reference = 'A' . date('y') . 'C0001';
  399. }
  400. $this->saveUpdate($order);
  401. }
  402. }
  403. // initInvoicePrices
  404. // updateInvoicePrices
  405. public function updateOrderInvoicePrices(Order $order, array $params = []): void
  406. {
  407. foreach ($order->productOrder as $productOrder) {
  408. if ($productOrder->product) {
  409. $this->productOrderBuilder->updateProductOrderInvoicePrice($productOrder, $params);
  410. }
  411. }
  412. }
  413. /**
  414. * Initialise les commandes liées au point de vente.
  415. */
  416. public function initPointSaleOrders(PointSale $pointSale, array $ordersArray): void
  417. {
  418. $pointSale->orders = [];
  419. $pointSale->revenues = 0;
  420. $pointSale->revenues_with_tax = 0;
  421. if ($ordersArray) {
  422. foreach ($ordersArray as $order) {
  423. $this->initOrder($order);
  424. if ($pointSale->id == $order->id_point_sale) {
  425. $pointSale->orders[] = $order;
  426. if($this->orderSolver->isOrderStatusValid($order)) {
  427. $pointSale->revenues += (float)$order->amount;
  428. $pointSale->revenues_with_tax += (float)$order->amount_with_tax;
  429. }
  430. }
  431. }
  432. }
  433. }
  434. public function assignAllOrdersDeliveryNote(array $idOrders, DeliveryNote $deliveryNote)
  435. {
  436. $this->unassignAllOrdersDeliveryNote($deliveryNote);
  437. foreach ($idOrders as $idOrder) {
  438. $order = $this->orderRepository->findOneOrderById((int)$idOrder);
  439. if ($order) {
  440. $this->assignOrderDeliveryNote($order, $deliveryNote);
  441. }
  442. }
  443. }
  444. public function assignOrderDeliveryNote(Order $order, DeliveryNote $deliveryNote)
  445. {
  446. if ($this->orderSolver->isOrderFromProducer($order)) {
  447. $this->updateOrderDeliveryNote($order, $deliveryNote);
  448. $order = $this->orderRepository->findOneOrderById($order->id);
  449. $user = $this->userRepository->findOneUserById($deliveryNote->id_user);
  450. $userProducer = $this->userProducerRepository->findOneUserProducer($user);
  451. $this->updateOrderInvoicePrices($order,
  452. [
  453. 'user' => $user,
  454. 'user_producer' => $userProducer,
  455. 'point_sale' => $order->pointSale
  456. ]);
  457. }
  458. }
  459. public function unassignAllOrdersDeliveryNote(DeliveryNote $deliveryNote)
  460. {
  461. Order::updateAll([
  462. 'id_delivery_note' => null
  463. ], [
  464. 'id_delivery_note' => $deliveryNote->id
  465. ]);
  466. }
  467. public function unassignAllOrdersInvoiceByDeliveryNote(DeliveryNote $deliveryNote)
  468. {
  469. Order::updateAll([
  470. 'id_invoice' => null
  471. ], [
  472. 'id_delivery_note' => $deliveryNote->id
  473. ]);
  474. }
  475. public function assignAllOrdersInvoiceByDeliveryNote(Invoice $invoice, DeliveryNote $deliveryNote)
  476. {
  477. Order::updateAll([
  478. 'id_invoice' => $invoice->id
  479. ], [
  480. 'id_delivery_note' => $deliveryNote->id
  481. ]);
  482. }
  483. public function updateOrderIgnoreWhenInvoicing(Order $order, bool $ignoreWhenInvoicing)
  484. {
  485. $order->ignore_when_invoicing = $ignoreWhenInvoicing;
  486. $this->update($order);
  487. }
  488. public function updateOrderDocument(Order $order, DocumentInterface $document = null)
  489. {
  490. if($this->documentSolver->isDocumentInvoice($document)) {
  491. $this->updateOrderInvoice($order, $document);
  492. }
  493. elseif($this->documentSolver->isDocumentDeliveryNote($document)) {
  494. $this->updateOrderDeliveryNote($order, $document);
  495. }
  496. }
  497. public function updateOrderInvoice(Order $order, Invoice $invoice = null)
  498. {
  499. if($invoice) {
  500. $order->populateInvoice($invoice);
  501. }
  502. else {
  503. $order->id_invoice = null;
  504. }
  505. $this->update($order);
  506. }
  507. public function updateOrderDeliveryNote(Order $order, DeliveryNote $deliveryNote = null)
  508. {
  509. if($deliveryNote) {
  510. $order->populateDeliveryNote($deliveryNote);
  511. }
  512. else {
  513. $order->id_delivery_note = null;
  514. }
  515. $this->update($order);
  516. }
  517. }