src/Verifactu/QuoteRequestReader.php line 42

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace FMS\eLibreria\CmsBundle\Verifactu;
  4. use Doctrine\ORM\EntityManagerInterface;
  5. use FMS\eLibreria\CoreBundle\Model\Entity\Client;
  6. use FMS\eLibreria\CoreBundle\Model\Entity\MyFiscalEntity;
  7. use FMS\eLibreria\CoreBundle\Model\Entity\PromoCode;
  8. use FMS\eLibreria\CoreBundle\Model\Entity\Tax;
  9. use FMS\eLibreria\CoreBundle\Model\Entity\WithHolding;
  10. use FMS\eLibreria\CoreBundle\Verifactu\Breakdown\ValueObject\AmountBasis;
  11. use FMS\eLibreria\CoreBundle\Verifactu\Breakdown\ValueObject\QuoteLine;
  12. use FMS\eLibreria\CoreBundle\Verifactu\Breakdown\ValueObject\QuoteRequest;
  13. use FMS\eLibreria\CoreBundle\Verifactu\Document\ValueObject\InvoiceType;
  14. use FMS\eLibreria\CoreBundle\Verifactu\Time\FiscalClock;
  15. /**
  16. * What a screen is asking about: the document as it stands there, not as the database has it (change
  17. * verifactu-03, design decision 15).
  18. *
  19. * A quote is asked for while somebody is typing, so nothing of it has been saved yet. What the browser sends
  20. * is what it **is** — how many, of what, with which tax — and never what it is worth: the amounts are the
  21. * server's, here as everywhere else.
  22. */
  23. final class QuoteRequestReader
  24. {
  25. public function __construct(
  26. private readonly EntityManagerInterface $entityManager,
  27. private readonly FiscalClock $clock,
  28. ) {
  29. }
  30. /**
  31. * @param array<string, mixed> $data
  32. */
  33. /**
  34. * @param bool $correctionByDifferences a corrective invoice by differences **is** the change, so its lines
  35. * and its groups may be negative (change verifactu-06, decision 3)
  36. */
  37. public function read(
  38. array $data,
  39. AmountBasis $basis,
  40. ?InvoiceType $invoiceType,
  41. bool $correctionByDifferences = false,
  42. ): QuoteRequest {
  43. $lines = [];
  44. $position = 0;
  45. foreach ($this->itemsOf($data) as $item) {
  46. $lines[] = new QuoteLine(
  47. $position++,
  48. (string) ($item['concept'] ?? ''),
  49. (string) ($item['units'] ?? '0'),
  50. (string) ($item['price'] ?? '0'),
  51. (string) ($item['discount'] ?? '0'),
  52. $this->find(Tax::class, $item['tax'] ?? null),
  53. $this->find(WithHolding::class, $item['withholding'] ?? null),
  54. );
  55. }
  56. return new QuoteRequest(
  57. $basis,
  58. $lines,
  59. $this->operationDate($data),
  60. $invoiceType,
  61. $this->find(MyFiscalEntity::class, $data['fiscalEntity'] ?? null),
  62. $this->find(Client::class, $data['client'] ?? null),
  63. $this->find(PromoCode::class, $data['promoCode'] ?? null),
  64. $correctionByDifferences,
  65. );
  66. }
  67. /**
  68. * @param array<string, mixed> $data
  69. *
  70. * @return list<array<string, mixed>>
  71. */
  72. private function itemsOf(array $data): array
  73. {
  74. $items = $data['items'] ?? [];
  75. return \is_array($items) ? array_values(array_filter($items, '\is_array')) : [];
  76. }
  77. /**
  78. * @param array<string, mixed> $data
  79. */
  80. private function operationDate(array $data): \DateTimeImmutable
  81. {
  82. $operationDate = $data['operationDate'] ?? null;
  83. if (\is_string($operationDate) && '' !== $operationDate) {
  84. $parsed = \DateTimeImmutable::createFromFormat('d/m/Y', $operationDate);
  85. if ($parsed instanceof \DateTimeImmutable) {
  86. return $parsed;
  87. }
  88. }
  89. return $this->clock->now();
  90. }
  91. /**
  92. * @template T of object
  93. *
  94. * @param class-string<T> $class
  95. *
  96. * @return T|null
  97. */
  98. private function find(string $class, mixed $hash): ?object
  99. {
  100. if (!\is_string($hash) || '' === $hash) {
  101. return null;
  102. }
  103. $found = $this->entityManager->getRepository($class)->findOneBy(['hash' => $hash]);
  104. return $found instanceof $class ? $found : null;
  105. }
  106. }