vendor/boldr/shop-bundle/src/EventSubscriber/InvoiceEventSubscriber.php line 143

Open in your IDE?
  1. <?php
  2. namespace Boldr\Shop\ShopBundle\EventSubscriber;
  3. use Doctrine\ORM\EntityManagerInterface;
  4. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  5. use Boldr\Shop\ShopBundle\Event\{ PaymentStatusUpdatedEvent, InvoiceProFormaUpdatedEvent, InvoiceStatusUpdatedEvent, PaymentCreatedEvent, InvoiceCreatedEvent };
  6. use Boldr\Shop\ShopBundle\Entity\{ Payment, Invoice };
  7. use Boldr\Shop\ShopBundle\OrderFlow\{ OrderFlowManagerInterface, OrderStateChangeProcessor };
  8. use Boldr\Shop\ShopBundle\Invoice\InvoiceNumberGeneratorInterface;
  9. use Boldr\Shop\ShopBundle\EmailFactory;
  10. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  11. use Symfony\Component\Mailer\MailerInterface;
  12. use DateTimeImmutable;
  13. class InvoiceEventSubscriber implements EventSubscriberInterface
  14. {
  15. private OrderFlowManagerInterface $orderFlowManager;
  16. private InvoiceNumberGeneratorInterface $invoiceNumberGenerator;
  17. private EventDispatcherInterface $eventDispatcher;
  18. private EntityManagerInterface $em;
  19. private OrderStateChangeProcessor $orderStateChangeProcessor;
  20. private EmailFactory $emailFactory;
  21. private MailerInterface $mailer;
  22. public function __construct(
  23. OrderFlowManagerInterface $orderFlowManager,
  24. InvoiceNumberGeneratorInterface $invoiceNumberGenerator,
  25. EventDispatcherInterface $eventDispatcher,
  26. OrderStateChangeProcessor $orderStateChangeProcessor,
  27. EntityManagerInterface $em,
  28. EmailFactory $emailFactory,
  29. MailerInterface $mailer
  30. )
  31. {
  32. $this->orderFlowManager = $orderFlowManager;
  33. $this->invoiceNumberGenerator = $invoiceNumberGenerator;
  34. $this->eventDispatcher = $eventDispatcher;
  35. $this->orderStateChangeProcessor = $orderStateChangeProcessor;
  36. $this->em = $em;
  37. $this->emailFactory = $emailFactory;
  38. $this->mailer = $mailer;
  39. }
  40. public static function getSubscribedEvents()
  41. {
  42. return [
  43. PaymentCreatedEvent::class => [
  44. ['onPaymentCreated', 10]
  45. ],
  46. PaymentStatusUpdatedEvent::class => [
  47. ['onPaymentStatusUpdated', 10]
  48. ],
  49. InvoiceCreatedEvent::class => [
  50. ['onInvoiceCreated', 10]
  51. ],
  52. InvoiceProFormaUpdatedEvent::class => [
  53. ['onInvoiceProFormaUpdated', 10]
  54. ],
  55. InvoiceStatusUpdatedEvent::class => [
  56. ['onInvoiceStatusUpdated', 10]
  57. ]
  58. ];
  59. }
  60. private function updateInvoiceStatus(Invoice $invoice, $newStatus)
  61. {
  62. if ($invoice->getStatus() !== $newStatus)
  63. {
  64. $event = new InvoiceStatusUpdatedEvent($invoice, $invoice->getStatus(), $newStatus);
  65. $invoice->setStatus($newStatus);
  66. $this->em->flush();
  67. $this->eventDispatcher->dispatch($event);
  68. }
  69. }
  70. private function handleInvoiceChange(Invoice $invoice)
  71. {
  72. // Generate invoice number if invoice is finished and agreed
  73. if ($invoice->getNumber() === null)
  74. {
  75. if ($invoice->isCreditInvoice() || $this->orderFlowManager->canGenerateInvoiceNumber($invoice))
  76. {
  77. $date = new DateTimeImmutable;
  78. $number = $invoice->isProForma() ? null : $this->invoiceNumberGenerator->generateInvoiceNumber($invoice->getCustomer(), $date);
  79. $invoice->finalize($number, $date);
  80. }
  81. }
  82. // Mark as paid if no amount left outstanding
  83. $newStatus = abs($invoice->getAmountOutstanding()) < 0.000001 ? Invoice::STATUS_PAID : ($invoice->isDraft() ? Invoice::STATUS_DRAFT : Invoice::STATUS_UNPAID);
  84. $this->updateInvoiceStatus($invoice, $newStatus);
  85. $this->em->flush();
  86. }
  87. public function onInvoiceProFormaUpdated(InvoiceProFormaUpdatedEvent $event)
  88. {
  89. $invoice = $event->getInvoice();
  90. $this->handleInvoiceChange($invoice);
  91. }
  92. public function onPaymentCreated(PaymentCreatedEvent $event)
  93. {
  94. $invoice = $event->getPayment()->getInvoice();
  95. $this->handleInvoiceChange($invoice);
  96. }
  97. public function onPaymentStatusUpdated(PaymentStatusUpdatedEvent $event)
  98. {
  99. $payment = $event->getPayment();
  100. if ($event->getNewStatus() === Payment::STATUS_FAILED)
  101. {
  102. $invoice = $payment->getInvoice();
  103. if ($invoice->getStatus() !== Invoice::STATUS_PAID)
  104. {
  105. $retryUrl = null;
  106. try
  107. {
  108. $email = $this->emailFactory->createPaymentFailedEmail($event->getPayment(), $retryUrl);
  109. $this->mailer->send($email);
  110. }
  111. catch (\Exception $ex)
  112. {
  113. // fail silently
  114. }
  115. }
  116. }
  117. else
  118. {
  119. $invoice = $event->getPayment()->getInvoice();
  120. $this->handleInvoiceChange($invoice);
  121. }
  122. }
  123. public function onInvoiceCreated(InvoiceCreatedEvent $event)
  124. {
  125. $invoice = $event->getInvoice();
  126. $this->handleInvoiceChange($invoice);
  127. }
  128. public function onInvoiceStatusUpdated(InvoiceStatusUpdatedEvent $event)
  129. {
  130. $invoice = $event->getInvoice();
  131. $orders = [];
  132. foreach ($invoice->getItems() as $invoiceItem)
  133. {
  134. $orders[] = $invoiceItem->getOrder();
  135. }
  136. $doneOrders = [];
  137. foreach ($orders as $order)
  138. {
  139. if (!in_array($order, $doneOrders))
  140. {
  141. $doneOrders[] = $order;
  142. $nextStates = $this->orderFlowManager->getNextStates($order);
  143. foreach ($nextStates as $nextState)
  144. {
  145. $changeStateSteps = $this->orderFlowManager->getChangeStateSteps($order, $nextState);
  146. if ($changeStateSteps->getAutomatic())
  147. {
  148. $doComplete = true;
  149. foreach ($changeStateSteps->getSteps() as $step)
  150. {
  151. if (!$step->isCompleted($order))
  152. {
  153. $doComplete = false;
  154. break;
  155. }
  156. }
  157. if ($doComplete)
  158. {
  159. $this->orderStateChangeProcessor->changeState($order, $nextState);
  160. break;
  161. }
  162. }
  163. }
  164. }
  165. }
  166. $this->em->flush();
  167. }
  168. }