Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/config/packages/backoffice_menu.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,14 @@ parameters:
- admin_accounting_payments_list
- admin_accounting_accounts_list
- admin_accounting_rules_list
compta_produits:
nom: 'Produits'
niveau: 'ROLE_ADMIN'
url: '/admin/accounting/produits'
extra_routes:
- admin_accounting_produits_list
- admin_accounting_produits_add
- admin_accounting_produits_edit
compta_recherche:
nom: 'Recherche comptable'
niveau: 'ROLE_ADMIN'
Expand Down
4 changes: 4 additions & 0 deletions app/config/routing/admin_accounting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ admin_accounting_journal:
resource: "admin_accounting/journal.yml"
prefix: /journal

admin_accounting_produits:
resource: "admin_accounting/produits.yml"
prefix: /produits

admin_accounting_quotations_list:
path: /quotations/list
defaults: {_controller: AppBundle\Controller\Admin\Accounting\Quotation\ListQuotationAction}
Expand Down
19 changes: 19 additions & 0 deletions app/config/routing/admin_accounting/produits.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
admin_accounting_produits_list:
path: /
defaults: {_controller: AppBundle\Controller\Admin\Accounting\Produit\ListProduitAction}

admin_accounting_produits_add:
path: /add
defaults: {_controller: AppBundle\Controller\Admin\Accounting\Produit\AddProduitAction}

admin_accounting_produits_edit:
path: /edit/{id}
defaults: {_controller: AppBundle\Controller\Admin\Accounting\Produit\EditProduitAction}
requirements:
id: '\d+'

admin_accounting_produits_delete:
path: /delete/{id}
defaults: {_controller: AppBundle\Controller\Admin\Accounting\Produit\DeleteProduitAction}
requirements:
id: '\d+'
19 changes: 19 additions & 0 deletions db/migrations/20260508153741_create_compta_produit_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

use Phinx\Migration\AbstractMigration;

final class CreateComptaProduitTable extends AbstractMigration
{
public function change(): void
{
$this->table('compta_produit')
->addColumn('reference', 'string', ['limit' => 255, 'null' => false])
->addColumn('designation', 'string', ['limit' => 255, 'null' => false])
->addColumn('quantite', 'integer', ['null' => true])
Comment thread
stakovicz marked this conversation as resolved.
->addColumn('prix_unitaire_ht', 'float', ['null' => false])
->addColumn('taux_tva', 'float', ['null' => true])
->create();
}
}
32 changes: 32 additions & 0 deletions sources/AppBundle/Accounting/Entity/Produit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace AppBundle\Accounting\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'compta_produit')]
class Produit
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
public ?int $id = null;

#[ORM\Column(length: 255, nullable: false)]
public string $reference;

#[ORM\Column(length: 255, nullable: false)]
public string $designation;

#[ORM\Column(nullable: true)]
public ?int $quantite = null;

#[ORM\Column(type: 'decimal', precision: 10, scale: 2, nullable: false)]
public float $prixUnitaireHt;

Check failure on line 28 in sources/AppBundle/Accounting/Entity/Produit.php

View workflow job for this annotation

GitHub Actions / PHPStan

Property AppBundle\Accounting\Entity\Produit::$prixUnitaireHt type mapping mismatch: database can contain string but property expects float.

#[ORM\Column(nullable: true)]
public ?float $tauxTva = null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace AppBundle\Accounting\Entity\Repository;

use AppBundle\Accounting\Entity\Produit;
use AppBundle\Doctrine\EntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
* @extends EntityRepository<Produit>
*/
final class ProduitRepository extends EntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Produit::class);
}

/**
* @return array<Produit>
*/
public function getAllSortedByReference(): array
{
return $this->createQueryBuilder('p')
->orderBy('p.reference', 'asc')
->getQuery()
->execute();
}
}
63 changes: 63 additions & 0 deletions sources/AppBundle/Accounting/Form/ProduitType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

namespace AppBundle\Accounting\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints as Assert;

class ProduitType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('reference', TextType::class, [
'label' => 'Référence',
'required' => true,
'constraints' => [
new Assert\NotBlank(),
new Assert\Type('string'),
new Assert\Length(max: 20),
Comment thread
stakovicz marked this conversation as resolved.
],
])->add('designation', TextareaType::class, [
'label' => 'Désignation',
'required' => true,
'constraints' => [
new Assert\NotBlank(),
new Assert\Type('string'),
new Assert\Length(max: 100),
Comment thread
Mopolo marked this conversation as resolved.
],
])->add('quantite', IntegerType::class, [
'label' => 'Quantité par défaut',
'required' => false,
'constraints' => [
new Assert\Positive(),
],
])->add('prixUnitaireHt', NumberType::class, [
'label' => 'Prix unitaire HT',
'required' => true,
'scale' => 2,
'constraints' => [
new Assert\NotBlank(),
new Assert\Positive(),
],
])->add('tauxTva', ChoiceType::class, [
'label' => 'Taux de TVA',
'required' => false,
'expanded' => true,
'multiple' => false,
'placeholder' => 'Non soumis',
'choices' => [
'5.5%' => 5.5,
'10%' => 10.0,
'20%' => 20.0,
Comment on lines +57 to +59
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion : Peut être travailler avec un Enum ou des constantes ou utiliser des taux qui existe déjà, non ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm je n'y avais pas pensé.

Je regarde et si ça fait trop de diff dans cette PR j'en ferais une autre pour ça pour le reste du code.

],
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\Accounting\Produit;

use AppBundle\Accounting\Entity\Produit;
use AppBundle\Accounting\Entity\Repository\ProduitRepository;
use AppBundle\Accounting\Form\ProduitType;
use AppBundle\AuditLog\Audit;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

final class AddProduitAction extends AbstractController
{
public function __construct(
private readonly ProduitRepository $produitRepository,
private readonly Audit $audit,
) {}

public function __invoke(Request $request): Response
{
$produit = new Produit();
$form = $this->createForm(ProduitType::class, $produit);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->produitRepository->save($produit);
$this->audit->log('Ajout du produit ' . $produit->reference);
$this->addFlash('notice', 'Le produit a été ajouté');
return $this->redirectToRoute('admin_accounting_produits_list');
}

return $this->render('admin/accounting/produit/form.html.twig', [
'form' => $form->createView(),
'produit' => $produit,
'formTitle' => 'Ajouter un produit',
'submitLabel' => 'Ajouter',
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\Accounting\Produit;

use AppBundle\Accounting\Entity\Produit;
use AppBundle\Accounting\Entity\Repository\ProduitRepository;
use AppBundle\AuditLog\Audit;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

final class DeleteProduitAction extends AbstractController
{
public function __construct(
private readonly ProduitRepository $produitRepository,
private readonly Audit $audit,
) {}

public function __invoke(Request $request, int $id): Response
{
$produit = $this->produitRepository->find($id);
if (!$produit instanceof Produit) {
$this->addFlash('error', 'Une erreur est survenue lors de la suppression du produit');
return $this->redirectToRoute('admin_accounting_produits_list');
}

$this->produitRepository->delete($produit);
$this->audit->log('Suppression du produit ' . $produit->reference);
$this->addFlash('notice', 'Le produit a été supprimé');
return $this->redirectToRoute('admin_accounting_produits_list');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\Accounting\Produit;

use AppBundle\Accounting\Entity\Produit;
use AppBundle\Accounting\Entity\Repository\ProduitRepository;
use AppBundle\Accounting\Form\ProduitType;
use AppBundle\AuditLog\Audit;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

final class EditProduitAction extends AbstractController
{
public function __construct(
private readonly ProduitRepository $produitRepository,
private readonly Audit $audit,
) {}

public function __invoke(int $id, Request $request): Response
{
$produit = $this->produitRepository->find($id);
Comment thread
Mopolo marked this conversation as resolved.
if (!$produit instanceof Produit) {
throw $this->createNotFoundException(sprintf('Le produit n\'a pas été trouvé avec l\'id "%s"', $id));
}

$form = $this->createForm(ProduitType::class, $produit);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$this->produitRepository->save($produit);
$this->audit->log('Modification du produit ' . $produit->reference);
$this->addFlash('notice', 'Le produit a été modifié');
return $this->redirectToRoute('admin_accounting_produits_list');
}

return $this->render('admin/accounting/produit/form.html.twig', [
'form' => $form->createView(),
'produit' => $produit,
'formTitle' => 'Modifier un produit',
'submitLabel' => 'Modifier',
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\Accounting\Produit;

use AppBundle\Accounting\Entity\Repository\ProduitRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Twig\Environment;

final readonly class ListProduitAction
{
public function __construct(
private ProduitRepository $produitRepository,
private Environment $twig,
) {}

public function __invoke(Request $request): Response
{
$produits = $this->produitRepository->getAllSortedByReference();

return new Response($this->twig->render('admin/accounting/produit/list.html.twig', [
'produits' => $produits,
]));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace AppBundle\Controller\Admin\Accounting\Quotation;

use Afup\Site\Comptabilite\Facture;
use AppBundle\Accounting\Entity\Repository\ProduitRepository;
use AppBundle\Accounting\Form\QuotationType;
use AppBundle\Accounting\Model\Invoicing;
use AppBundle\Accounting\Model\InvoicingDetail;
Expand All @@ -20,6 +21,7 @@ public function __construct(
private readonly InvoicingRepository $invoicingRepository,
private readonly Facture $facture,
private readonly InvoicingDetailRepository $invoicingDetailRepository,
private readonly ProduitRepository $produitRepository,
) {}

public function __invoke(Request $request): Response
Expand Down Expand Up @@ -49,6 +51,7 @@ public function __invoke(Request $request): Response
'quotation' => $quotation,
'form' => $form->createView(),
'submitLabel' => 'Ajouter',
'produits' => $this->produitRepository->getAllSortedByReference(),
]);
}

Expand All @@ -59,7 +62,6 @@ private function init(int $quotationId): Invoicing
$quotation = new Invoicing();
$quotation->setQuotationDate(new \DateTime());
$quotation->setCountryId('FR');
$quotation->setDetails([new InvoicingDetail()]);

return $quotation;
}
Expand Down
Loading
Loading