diff --git a/app/config/packages/backoffice_menu.yaml b/app/config/packages/backoffice_menu.yaml index e12d71d71..24ee95f83 100644 --- a/app/config/packages/backoffice_menu.yaml +++ b/app/config/packages/backoffice_menu.yaml @@ -236,6 +236,7 @@ parameters: niveau: 'ROLE_ADMIN' extra_routes: - admin_accounting_invoices_list + - admin_accounting_invoices_edit compta_journal: nom: 'Journal' url: '/admin/accounting/journal/list' diff --git a/app/config/routing/admin_accounting.yml b/app/config/routing/admin_accounting.yml index 181bae4fd..c4d615ea4 100644 --- a/app/config/routing/admin_accounting.yml +++ b/app/config/routing/admin_accounting.yml @@ -34,6 +34,10 @@ admin_accounting_invoices_list: path: /invoices/list defaults: {_controller: AppBundle\Controller\Admin\Accounting\Invoice\ListInvoiceAction} +admin_accounting_invoices_edit: + path: /invoices/edit + defaults: {_controller: AppBundle\Controller\Admin\Accounting\Invoice\EditInvoiceAction} + admin_accounting_invoices_download: path: /invoices/download defaults: {_controller: AppBundle\Controller\Admin\Accounting\Invoice\DownloadInvoiceAction} diff --git a/sources/AppBundle/Accounting/Form/InvoiceType.php b/sources/AppBundle/Accounting/Form/InvoiceType.php new file mode 100644 index 000000000..05a4dcca2 --- /dev/null +++ b/sources/AppBundle/Accounting/Form/InvoiceType.php @@ -0,0 +1,149 @@ +add('invoiceDate', DateType::class, [ + 'label' => 'Date facture', + 'widget' => 'single_text', + ])->add('company', TextType::class, [ + 'label' => 'Société', + 'constraints' => [ + new Assert\NotBlank(), + new Assert\Length(max: 50), + ], + ])->add('service', TextType::class, [ + 'label' => 'Service', + 'required' => false, + 'constraints' => [ + new Assert\Length(max: 50), + ], + ])->add('address', TextareaType::class, [ + 'label' => 'Adresse', + ])->add('zipcode', TextType::class, [ + 'label' => 'Code postal', + 'constraints' => [ + new Assert\NotBlank(), + new Assert\Length(max: 10), + ], + ])->add('city', TextType::class, [ + 'label' => 'Ville', + 'constraints' => [ + new Assert\NotBlank(), + new Assert\Length(max: 50), + ], + ])->add('countryId', ChoiceType::class, [ + 'label' => 'Pays', + 'choices' => array_flip($this->pays->obtenirPays()), + ])->add('lastname', TextType::class, [ + 'label' => 'Nom', + 'required' => false, + 'constraints' => [ + new Assert\Length(max: 50), + ], + ])->add('firstname', TextType::class, [ + 'label' => 'Prénom', + 'required' => false, + 'constraints' => [ + new Assert\Length(max: 50), + ], + ])->add('phone', TextType::class, [ + 'label' => 'Tel', + 'required' => false, + 'constraints' => [ + new Assert\Length(max: 30), + ], + ])->add('email', EmailType::class, [ + 'label' => 'Email (facture)', + 'constraints' => [ + new Assert\NotBlank(), + new Assert\Length(max: 100), + ], + ])->add('tvaIntra', TextType::class, [ + 'label' => 'TVA intracommunautaire (facture)', + 'required' => false, + 'constraints' => [ + new Assert\Length(max: 20), + ], + ])->add('refClt1', TextType::class, [ + 'label' => 'Référence client', + 'required' => false, + 'constraints' => [ + new Assert\Length(max: 50), + ], + ])->add('refClt2', TextType::class, [ + 'label' => 'Référence client 2', + 'required' => false, + 'constraints' => [ + new Assert\Length(max: 50), + ], + ])->add('refClt3', TextType::class, [ + 'label' => 'Référence client 3', + 'required' => false, + 'constraints' => [ + new Assert\Length(max: 50), + ], + ])->add('observation', TextareaType::class, [ + 'required' => false, + 'label' => 'Observation', + ])->add('currency', EnumType::class, [ + 'required' => false, + 'class' => InvoicingCurrency::class, + 'attr' => ['size' => count(InvoicingCurrency::cases())], + 'label' => 'Monnaie de la facture', + 'placeholder' => false, + ])->add('details', CollectionType::class, [ + 'entry_type' => InvoicingRowType::class, + 'keep_as_list' => true, + 'allow_add' => false, + 'allow_delete' => false, + ])->add('quotationNumber', TextType::class, [ + 'label' => 'Numéro de devis', + 'required' => false, + 'attr' => ['readonly' => 'readonly'], + 'constraints' => [ + new Assert\Length(max: 50), + ], + ])->add('invoiceNumber', TextType::class, [ + 'label' => 'Numéro facture', + 'required' => false, + 'attr' => ['readonly' => 'readonly'], + 'constraints' => [ + new Assert\Length(max: 50), + ], + ])->add('paymentStatus', EnumType::class, [ + 'required' => false, + 'class' => InvoicingPaymentStatus::class, + 'attr' => ['size' => count(InvoicingPaymentStatus::cases())], + 'label' => 'État paiement', + 'placeholder' => false, + 'choice_label' => fn(InvoicingPaymentStatus $choice, string $key, mixed $value): string => $choice->label(), + ]) + ->add('paymentDate', DateType::class, [ + 'label' => 'Date de paiement', + 'required' => false, + 'widget' => 'single_text', + ]); + } +} diff --git a/sources/AppBundle/Accounting/InvoicingPaymentStatus.php b/sources/AppBundle/Accounting/InvoicingPaymentStatus.php index 8674d7ae9..48a607ccb 100644 --- a/sources/AppBundle/Accounting/InvoicingPaymentStatus.php +++ b/sources/AppBundle/Accounting/InvoicingPaymentStatus.php @@ -9,4 +9,13 @@ enum InvoicingPaymentStatus: int case Waiting = 0; case Payed = 1; case Cancelled = 2; + + public function label(): string + { + return match ($this) { + self::Waiting => 'En attente de paiement', + self::Payed => 'Payé', + self::Cancelled => 'Annulé', + }; + } } diff --git a/sources/AppBundle/Accounting/Model/Invoicing.php b/sources/AppBundle/Accounting/Model/Invoicing.php index 38b519048..2914f8a1f 100644 --- a/sources/AppBundle/Accounting/Model/Invoicing.php +++ b/sources/AppBundle/Accounting/Model/Invoicing.php @@ -6,6 +6,7 @@ use Afup\Site\Utils\Utils; use AppBundle\Accounting\InvoicingCurrency; +use AppBundle\Accounting\InvoicingPaymentStatus; use CCMBenchmark\Ting\Entity\NotifyProperty; use CCMBenchmark\Ting\Entity\NotifyPropertyInterface; use DateTime; @@ -34,7 +35,7 @@ class Invoicing implements NotifyPropertyInterface private string $lastname = ''; private string $firstname = ''; private string $phone = ''; - private int $paymentStatus = 0; + private InvoicingPaymentStatus $paymentStatus = InvoicingPaymentStatus::Waiting; private ?DateTime $paymentDate = null; private ?InvoicingCurrency $currency = null; /** @var InvoicingDetail[] */ @@ -301,12 +302,12 @@ public function setPhone(string $phone): self return $this; } - public function getPaymentStatus(): int + public function getPaymentStatus(): InvoicingPaymentStatus { return $this->paymentStatus; } - public function setPaymentStatus(int $paymentStatus): self + public function setPaymentStatus(InvoicingPaymentStatus $paymentStatus): self { $this->propertyChanged('paymentStatus', $this->paymentStatus, $paymentStatus); $this->paymentStatus = $paymentStatus; diff --git a/sources/AppBundle/Accounting/Model/Repository/InvoicingRepository.php b/sources/AppBundle/Accounting/Model/Repository/InvoicingRepository.php index 2f32109cc..78d2855e3 100644 --- a/sources/AppBundle/Accounting/Model/Repository/InvoicingRepository.php +++ b/sources/AppBundle/Accounting/Model/Repository/InvoicingRepository.php @@ -4,6 +4,7 @@ namespace AppBundle\Accounting\Model\Repository; +use AppBundle\Accounting\InvoicingPaymentStatus; use CCMBenchmark\Ting\Repository\Hydrator\AggregateFrom; use CCMBenchmark\Ting\Repository\Hydrator\AggregateTo; use CCMBenchmark\Ting\Repository\Hydrator\RelationMany; @@ -25,21 +26,21 @@ */ class InvoicingRepository extends Repository implements MetadataInitializer { - public function getQuotationById(int $periodId): ?Invoicing + public function getById(int $id): ?Invoicing { /** @var Select $builder */ $builder = $this->getQueryBuilder(self::QUERY_SELECT); $builder->cols(['acf.*', 'acfd.*']) ->from('afup_compta_facture acf') ->leftJoin('afup_compta_facture_details acfd', 'acfd.idafup_compta_facture = acf.id') - ->where('acf.id = :periodId'); + ->where('acf.id = :id'); $hydrator = new HydratorRelational(); $hydrator->addRelation(new RelationMany(new AggregateFrom('acfd'), new AggregateTo('acf'), 'setDetails')); $hydrator->callableFinalizeAggregate(fn(array $row) => $row['acf']); $collection = $this->getQuery($builder->getStatement()) - ->setParams(['periodId' => $periodId]) + ->setParams(['id' => $id]) ->query($this->getCollection($hydrator)); if ($collection->count() === 0) { @@ -240,7 +241,11 @@ public static function initMetadata(SerializerFactoryInterface $serializerFactor ->addField([ 'columnName' => 'etat_paiement', 'fieldName' => 'paymentStatus', - 'type' => 'int', + 'type' => 'enum', + 'serializer' => BackedEnum::class, + 'serializer_options' => [ + 'unserialize' => ['enum' => InvoicingPaymentStatus::class], + ], ]) ->addField([ 'columnName' => 'date_paiement', diff --git a/sources/AppBundle/Controller/Admin/Accounting/Invoice/EditInvoiceAction.php b/sources/AppBundle/Controller/Admin/Accounting/Invoice/EditInvoiceAction.php new file mode 100644 index 000000000..a3dda4a3c --- /dev/null +++ b/sources/AppBundle/Controller/Admin/Accounting/Invoice/EditInvoiceAction.php @@ -0,0 +1,54 @@ +query->getInt('invoiceId'); + $invoice = $this->invoicingRepository->getById($invoiceId); + if (!$invoice instanceof Invoicing) { + throw $this->createNotFoundException("Cette facture n'existe pas"); + } + + $form = $this->createForm(InvoiceType::class, $invoice); + $form->handleRequest($request); + if ($form->isSubmitted() && $form->isValid()) { + try { + $this->invoicingRepository->startTransaction(); + foreach ($invoice->getDetails() as $detail) { + $this->invoicingDetailRepository->save($detail); + } + $this->invoicingRepository->save($invoice); + $this->invoicingRepository->commit(); + $this->addFlash('success', 'L\'écriture a été modifiée'); + return $this->redirectToRoute('admin_accounting_invoices_list'); + } catch (\Exception $e) { + $this->invoicingRepository->rollback(); + $this->addFlash('error', 'L\'écriture n\'a pas pu être enregistrée'); + } + } + + return $this->render('admin/accounting/invoice/edit.html.twig', [ + 'invoice' => $invoice, + 'form' => $form->createView(), + 'submitLabel' => 'Modifier', + ]); + } +} diff --git a/sources/AppBundle/Controller/Admin/Accounting/Invoice/ListInvoiceAction.php b/sources/AppBundle/Controller/Admin/Accounting/Invoice/ListInvoiceAction.php index 469b9f29f..484557269 100644 --- a/sources/AppBundle/Controller/Admin/Accounting/Invoice/ListInvoiceAction.php +++ b/sources/AppBundle/Controller/Admin/Accounting/Invoice/ListInvoiceAction.php @@ -38,7 +38,7 @@ public function __invoke(Request $request): Response /** @var Invoicing $invoice */ foreach ($invoices as $invoice) { - if ($invoice->getPaymentStatus() === InvoicingPaymentStatus::Cancelled->value) { + if ($invoice->getPaymentStatus() === InvoicingPaymentStatus::Cancelled) { continue; } diff --git a/sources/AppBundle/Controller/Admin/Accounting/Quotation/AddQuotationAction.php b/sources/AppBundle/Controller/Admin/Accounting/Quotation/AddQuotationAction.php index 469bdb9ad..bfe8f8e0b 100644 --- a/sources/AppBundle/Controller/Admin/Accounting/Quotation/AddQuotationAction.php +++ b/sources/AppBundle/Controller/Admin/Accounting/Quotation/AddQuotationAction.php @@ -54,7 +54,7 @@ public function __invoke(Request $request): Response private function init(int $quotationId): Invoicing { - $baseQuotation = $this->invoicingRepository->getQuotationById($quotationId); + $baseQuotation = $this->invoicingRepository->getById($quotationId); if (!$baseQuotation instanceof Invoicing) { $quotation = new Invoicing(); $quotation->setQuotationDate(new \DateTime()); diff --git a/sources/AppBundle/Controller/Admin/Accounting/Quotation/EditQuotationAction.php b/sources/AppBundle/Controller/Admin/Accounting/Quotation/EditQuotationAction.php index 4daa9e0b7..01506d042 100644 --- a/sources/AppBundle/Controller/Admin/Accounting/Quotation/EditQuotationAction.php +++ b/sources/AppBundle/Controller/Admin/Accounting/Quotation/EditQuotationAction.php @@ -21,7 +21,7 @@ public function __construct( public function __invoke(Request $request): Response { $quotationId = $request->query->getInt('quotationId'); - $quotation = $this->invoicingRepository->getQuotationById($quotationId); + $quotation = $this->invoicingRepository->getById($quotationId); if ($quotation === null) { throw $this->createNotFoundException("Ce devis n'existe pas"); } diff --git a/templates/admin/accounting/invoice/edit.html.twig b/templates/admin/accounting/invoice/edit.html.twig new file mode 100644 index 000000000..d40a6424d --- /dev/null +++ b/templates/admin/accounting/invoice/edit.html.twig @@ -0,0 +1,175 @@ +{% extends 'admin/base_with_header.html.twig' %} + +{% block content %} +
+ * indique un champ obligatoire +
+