<?php
declare(strict_types=1);
namespace FMS\eLibreria\CmsBundle\Verifactu;
use Doctrine\ORM\EntityManagerInterface;
use FMS\eLibreria\CoreBundle\Model\Entity\Client;
use FMS\eLibreria\CoreBundle\Model\Entity\MyFiscalEntity;
use FMS\eLibreria\CoreBundle\Model\Entity\PromoCode;
use FMS\eLibreria\CoreBundle\Model\Entity\Tax;
use FMS\eLibreria\CoreBundle\Model\Entity\WithHolding;
use FMS\eLibreria\CoreBundle\Verifactu\Breakdown\ValueObject\AmountBasis;
use FMS\eLibreria\CoreBundle\Verifactu\Breakdown\ValueObject\QuoteLine;
use FMS\eLibreria\CoreBundle\Verifactu\Breakdown\ValueObject\QuoteRequest;
use FMS\eLibreria\CoreBundle\Verifactu\Document\ValueObject\InvoiceType;
use FMS\eLibreria\CoreBundle\Verifactu\Time\FiscalClock;
/**
* What a screen is asking about: the document as it stands there, not as the database has it (change
* verifactu-03, design decision 15).
*
* A quote is asked for while somebody is typing, so nothing of it has been saved yet. What the browser sends
* is what it **is** — how many, of what, with which tax — and never what it is worth: the amounts are the
* server's, here as everywhere else.
*/
final class QuoteRequestReader
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly FiscalClock $clock,
) {
}
/**
* @param array<string, mixed> $data
*/
/**
* @param bool $correctionByDifferences a corrective invoice by differences **is** the change, so its lines
* and its groups may be negative (change verifactu-06, decision 3)
*/
public function read(
array $data,
AmountBasis $basis,
?InvoiceType $invoiceType,
bool $correctionByDifferences = false,
): QuoteRequest {
$lines = [];
$position = 0;
foreach ($this->itemsOf($data) as $item) {
$lines[] = new QuoteLine(
$position++,
(string) ($item['concept'] ?? ''),
(string) ($item['units'] ?? '0'),
(string) ($item['price'] ?? '0'),
(string) ($item['discount'] ?? '0'),
$this->find(Tax::class, $item['tax'] ?? null),
$this->find(WithHolding::class, $item['withholding'] ?? null),
);
}
return new QuoteRequest(
$basis,
$lines,
$this->operationDate($data),
$invoiceType,
$this->find(MyFiscalEntity::class, $data['fiscalEntity'] ?? null),
$this->find(Client::class, $data['client'] ?? null),
$this->find(PromoCode::class, $data['promoCode'] ?? null),
$correctionByDifferences,
);
}
/**
* @param array<string, mixed> $data
*
* @return list<array<string, mixed>>
*/
private function itemsOf(array $data): array
{
$items = $data['items'] ?? [];
return \is_array($items) ? array_values(array_filter($items, '\is_array')) : [];
}
/**
* @param array<string, mixed> $data
*/
private function operationDate(array $data): \DateTimeImmutable
{
$operationDate = $data['operationDate'] ?? null;
if (\is_string($operationDate) && '' !== $operationDate) {
$parsed = \DateTimeImmutable::createFromFormat('d/m/Y', $operationDate);
if ($parsed instanceof \DateTimeImmutable) {
return $parsed;
}
}
return $this->clock->now();
}
/**
* @template T of object
*
* @param class-string<T> $class
*
* @return T|null
*/
private function find(string $class, mixed $hash): ?object
{
if (!\is_string($hash) || '' === $hash) {
return null;
}
$found = $this->entityManager->getRepository($class)->findOneBy(['hash' => $hash]);
return $found instanceof $class ? $found : null;
}
}