diff --git a/sources/AppBundle/Accounting/Form/QuotationType.php b/sources/AppBundle/Accounting/Form/QuotationType.php index 01d2a1302..523b8dbc5 100644 --- a/sources/AppBundle/Accounting/Form/QuotationType.php +++ b/sources/AppBundle/Accounting/Form/QuotationType.php @@ -151,6 +151,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'keep_as_list' => true, 'allow_add' => true, 'allow_delete' => true, + 'constraints' => [new Assert\Valid()], ]); if ($options['actionType'] === 'edit') { diff --git a/sources/AppBundle/Accounting/Model/Invoicing.php b/sources/AppBundle/Accounting/Model/Invoicing.php index dd5c4fe8d..2914f8a1f 100644 --- a/sources/AppBundle/Accounting/Model/Invoicing.php +++ b/sources/AppBundle/Accounting/Model/Invoicing.php @@ -10,7 +10,6 @@ use CCMBenchmark\Ting\Entity\NotifyProperty; use CCMBenchmark\Ting\Entity\NotifyPropertyInterface; use DateTime; -use Symfony\Component\Validator\Constraints as Assert; class Invoicing implements NotifyPropertyInterface { @@ -40,7 +39,6 @@ class Invoicing implements NotifyPropertyInterface private ?DateTime $paymentDate = null; private ?InvoicingCurrency $currency = null; /** @var InvoicingDetail[] */ - #[Assert\Valid] private array $details = []; private ?float $price = null; diff --git a/tests/unit/AppBundle/Accounting/Form/InvoiceTypeTest.php b/tests/unit/AppBundle/Accounting/Form/InvoiceTypeTest.php new file mode 100644 index 000000000..4cb4a8cc9 --- /dev/null +++ b/tests/unit/AppBundle/Accounting/Form/InvoiceTypeTest.php @@ -0,0 +1,134 @@ +createMock(Pays::class); + $pays->method('obtenirPays')->willReturn(['FR' => 'France']); + + return [ + new PreloadedExtension([new InvoiceType($pays)], []), + // TypeTestCase ne la charge pas, or l'option `constraints` en dépend. + new ValidatorExtension(Validation::createValidatorBuilder()->enableAttributeMapping()->getValidator()), + ]; + } + + #[Test] + public function uneFacturePeutEtreMarqueePayee(): void + { + $invoice = $this->newInvoice([$this->detail('SPONSO', 'Sponsoring bronze')]); + $form = $this->factory->create(InvoiceType::class, $invoice); + + $form->submit($this->payload()); + + self::assertTrue($form->isValid(), (string) $form->getErrors(true, false)); + self::assertSame(InvoicingPaymentStatus::Payed, $invoice->getPaymentStatus()); + } + + /** + * Le back-office historique créait systématiquement cinq lignes de détail, vides + * comprises. Ces lignes ne sont pas éditables depuis l'écran facture : les valider + * rendrait ces factures définitivement non modifiables. + */ + #[Test] + public function uneFactureAvecDesLignesVidesHeriteesPeutEtreMarqueePayee(): void + { + $invoice = $this->newInvoice([ + $this->detail('SPONSO', 'Sponsoring bronze'), + $this->detail('', ''), + $this->detail('', ''), + ]); + $form = $this->factory->create(InvoiceType::class, $invoice); + + $form->submit($this->payload()); + + self::assertTrue($form->isValid(), (string) $form->getErrors(true, false)); + self::assertSame(InvoicingPaymentStatus::Payed, $invoice->getPaymentStatus()); + } + + /** + * @return array + */ + private function payload(): array + { + return [ + 'invoiceDate' => '2026-01-15', + 'company' => 'ACME', + 'service' => '', + 'address' => '1 rue du test', + 'zipcode' => '69000', + 'city' => 'Lyon', + 'countryId' => 'FR', + 'lastname' => '', + 'firstname' => '', + 'phone' => '', + 'email' => 'compta@example.com', + 'tvaIntra' => '', + 'refClt1' => '', + 'refClt2' => '', + 'refClt3' => '', + 'observation' => '', + 'currency' => 'EUR', + 'quotationNumber' => '2026-001', + 'invoiceNumber' => '2026-001', + 'paymentStatus' => (string) InvoicingPaymentStatus::Payed->value, + 'paymentDate' => '2026-02-01', + ]; + } + + /** + * @param list $details + */ + private function newInvoice(array $details): Invoicing + { + $invoice = new Invoicing(); + $invoice->setId(42); + $invoice->setCompany('ACME'); + $invoice->setAddress('1 rue du test'); + $invoice->setZipcode('69000'); + $invoice->setCity('Lyon'); + $invoice->setCountryId('FR'); + $invoice->setEmail('compta@example.com'); + $invoice->setQuotationNumber('2026-001'); + $invoice->setInvoiceNumber('2026-001'); + $invoice->setInvoiceDate(new \DateTime('2026-01-15')); + $invoice->setPaymentStatus(InvoicingPaymentStatus::Waiting); + $invoice->setDetails($details); + + return $invoice; + } + + private function detail(string $reference, string $designation): InvoicingDetail + { + $detail = new InvoicingDetail(); + $detail->setReference($reference); + $detail->setDesignation($designation); + $detail->setQuantity(0.0); + $detail->setUnitPrice(0.0); + $detail->setTva(0.0); + + return $detail; + } +}