diff --git a/migrations/Version20260402204223.php b/migrations/Version20260402204223.php new file mode 100644 index 0000000..bcdf45c --- /dev/null +++ b/migrations/Version20260402204223.php @@ -0,0 +1,39 @@ +addSql('ALTER TABLE price_automatic ADD month_price NUMERIC(10, 2) DEFAULT \'0.00\' NOT NULL'); + $this->addSql('ALTER TABLE price_automatic ADD period SMALLINT DEFAULT 1 NOT NULL'); + $this->addSql('ALTER TABLE price_automatic ADD stripe_id VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE price_automatic ADD stripe_abonnement_id VARCHAR(255) DEFAULT NULL'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_FAD167EC8CDE5729 ON price_automatic (type)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP INDEX UNIQ_FAD167EC8CDE5729'); + $this->addSql('ALTER TABLE price_automatic DROP month_price'); + $this->addSql('ALTER TABLE price_automatic DROP period'); + $this->addSql('ALTER TABLE price_automatic DROP stripe_id'); + $this->addSql('ALTER TABLE price_automatic DROP stripe_abonnement_id'); + } +} diff --git a/src/Controller/Admin/TarificationController.php b/src/Controller/Admin/TarificationController.php new file mode 100644 index 0000000..d68c58c --- /dev/null +++ b/src/Controller/Admin/TarificationController.php @@ -0,0 +1,54 @@ +ensureDefaultPrices(); + foreach ($created as $type) { + $this->addFlash('success', 'Tarif "'.$type.'" cree automatiquement.'); + } + + return $this->render('admin/tarification/index.html.twig', [ + 'prices' => $tarification->getAll(), + ]); + } + + #[Route('/{id}/edit', name: '_edit', methods: ['POST'])] + public function edit(int $id, Request $request, PriceAutomaticRepository $repository, EntityManagerInterface $em): Response + { + $price = $repository->find($id); + + if (null === $price) { + throw $this->createNotFoundException('Tarif introuvable.'); + } + + $price->setTitle(trim($request->request->getString('title'))); + $price->setDescription(trim($request->request->getString('description')) ?: null); + $price->setPriceHt($request->request->getString('priceHt')); + $price->setMonthPrice($request->request->getString('monthPrice')); + $price->setPeriod((int) $request->request->getString('period')); + $price->setStripeId(trim($request->request->getString('stripeId')) ?: null); + $price->setStripeAbonnementId(trim($request->request->getString('stripeAbonnementId')) ?: null); + + $em->flush(); + + $this->addFlash('success', 'Tarif "'.$price->getType().'" mis a jour.'); + + return $this->redirectToRoute('app_admin_tarification'); + } +} diff --git a/src/Entity/PriceAutomatic.php b/src/Entity/PriceAutomatic.php index 7f0a60f..7becd67 100644 --- a/src/Entity/PriceAutomatic.php +++ b/src/Entity/PriceAutomatic.php @@ -13,7 +13,7 @@ class PriceAutomatic #[ORM\Column] private ?int $id = null; - #[ORM\Column(length: 50)] + #[ORM\Column(length: 50, unique: true)] private string $type; #[ORM\Column(length: 255)] @@ -25,6 +25,18 @@ class PriceAutomatic #[ORM\Column(type: 'decimal', precision: 10, scale: 2)] private string $priceHt; + #[ORM\Column(type: 'decimal', precision: 10, scale: 2, options: ['default' => '0.00'])] + private string $monthPrice = '0.00'; + + #[ORM\Column(type: 'smallint', options: ['default' => 1])] + private int $period = 1; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $stripeId = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $stripeAbonnementId = null; + public function getId(): ?int { return $this->id; @@ -69,4 +81,44 @@ class PriceAutomatic { $this->priceHt = $priceHt; } + + public function getMonthPrice(): string + { + return $this->monthPrice; + } + + public function setMonthPrice(string $monthPrice): void + { + $this->monthPrice = $monthPrice; + } + + public function getPeriod(): int + { + return $this->period; + } + + public function setPeriod(int $period): void + { + $this->period = $period; + } + + public function getStripeId(): ?string + { + return $this->stripeId; + } + + public function setStripeId(?string $stripeId): void + { + $this->stripeId = $stripeId; + } + + public function getStripeAbonnementId(): ?string + { + return $this->stripeAbonnementId; + } + + public function setStripeAbonnementId(?string $stripeAbonnementId): void + { + $this->stripeAbonnementId = $stripeAbonnementId; + } } diff --git a/src/Service/TarificationService.php b/src/Service/TarificationService.php new file mode 100644 index 0000000..066d0ce --- /dev/null +++ b/src/Service/TarificationService.php @@ -0,0 +1,185 @@ + [ + 'title' => 'Esy-Web Business', + 'description' => 'Licence Esy-Web + installation technique. Le client remplit le site en autonomie.', + 'priceHt' => '500.00', + 'monthPrice' => '100.00', + 'period' => 1, + ], + 'esyweb_premium' => [ + 'title' => 'Esy-Web Premium', + 'description' => 'Creation complete du site par SITECONSEIL avec accompagnement jusqu\'a la mise en ligne.', + 'priceHt' => '3200.00', + 'monthPrice' => '100.00', + 'period' => 1, + ], + 'ecommerce_business' => [ + 'title' => 'E-Commerce Business', + 'description' => 'Licence Esy-Web full options + E-boutique. Produits illimites.', + 'priceHt' => '999.00', + 'monthPrice' => '150.00', + 'period' => 1, + ], + 'ecommerce_premium' => [ + 'title' => 'E-Commerce Premium', + 'description' => 'Creation complete du site E-Commerce par SITECONSEIL. Produits illimites.', + 'priceHt' => '5110.00', + 'monthPrice' => '150.00', + 'period' => 1, + ], + 'esymail' => [ + 'title' => 'Esy-Mail', + 'description' => 'Messagerie professionnelle. 2 boites mail, 5 Go, antispam, RGPD.', + 'priceHt' => '50.00', + 'monthPrice' => '30.00', + 'period' => 1, + ], + 'esymailer' => [ + 'title' => 'Esy-Mailer', + 'description' => 'Envoi de mail en masse. 15 000 mails/mois.', + 'priceHt' => '50.00', + 'monthPrice' => '30.00', + 'period' => 1, + ], + 'esydefender_pro' => [ + 'title' => 'Esy-Defender Pro', + 'description' => 'Cyber defense avancee. Anti-DDoS, pare-feu, filtrage geo, surveillance.', + 'priceHt' => '50.00', + 'monthPrice' => '60.00', + 'period' => 3, + ], + 'esymeet' => [ + 'title' => 'Esy-Meet', + 'description' => 'Prise de rendez-vous en ligne via Cal.com.', + 'priceHt' => '50.00', + 'monthPrice' => '30.00', + 'period' => 1, + ], + 'esytchat' => [ + 'title' => 'Esy-Tchat', + 'description' => 'Chat en ligne sur votre site via Chatwoot.', + 'priceHt' => '50.00', + 'monthPrice' => '15.00', + 'period' => 1, + ], + 'esycreator' => [ + 'title' => 'Esy-Creator', + 'description' => 'Maintenance graphique et editoriale du site.', + 'priceHt' => '500.00', + 'monthPrice' => '100.00', + 'period' => 3, + ], + 'ndd_depot' => [ + 'title' => 'Nom de domaine - Depot', + 'description' => 'Enregistrement initial avec SPF, DMARC, DNSSEC, Whois, HTTPS.', + 'priceHt' => '20.00', + 'monthPrice' => '0.00', + 'period' => 1, + ], + 'ndd_renouvellement' => [ + 'title' => 'Nom de domaine - Renouvellement', + 'description' => 'Renouvellement annuel du nom de domaine.', + 'priceHt' => '20.00', + 'monthPrice' => '0.00', + 'period' => 12, + ], + 'ndd_gestion' => [ + 'title' => 'Nom de domaine - Gestion', + 'description' => 'Gestion technique DNS, HTTPS, redirections, surveillance.', + 'priceHt' => '30.00', + 'monthPrice' => '0.00', + 'period' => 12, + ], + 'ndd_reactivation' => [ + 'title' => 'Nom de domaine - Reactivation', + 'description' => 'Reactivation d\'un nom de domaine expire.', + 'priceHt' => '50.00', + 'monthPrice' => '0.00', + 'period' => 1, + ], + 'formation_pack10h' => [ + 'title' => 'Pack 10 heures de formation', + 'description' => 'Construction du site ensemble avec formation.', + 'priceHt' => '500.00', + 'monthPrice' => '0.00', + 'period' => 1, + ], + 'formation_heure' => [ + 'title' => 'Formation a la demande', + 'description' => 'Formation individuelle a l\'heure.', + 'priceHt' => '70.00', + 'monthPrice' => '0.00', + 'period' => 1, + ], + ]; + + public function __construct( + private PriceAutomaticRepository $repository, + private EntityManagerInterface $em, + ) { + } + + /** + * Verifie que tous les tarifs par defaut existent, cree ceux qui manquent. + * + * @return list liste des types crees + */ + public function ensureDefaultPrices(): array + { + $existing = $this->repository->findAll(); + $existingTypes = array_map(fn (PriceAutomatic $p) => $p->getType(), $existing); + $created = []; + + foreach (self::DEFAULT_PRICES as $type => $data) { + if (!\in_array($type, $existingTypes, true)) { + $price = new PriceAutomatic(); + $price->setType($type); + $price->setTitle($data['title']); + $price->setDescription($data['description']); + $price->setPriceHt($data['priceHt']); + $price->setMonthPrice($data['monthPrice']); + $price->setPeriod($data['period']); + + $this->em->persist($price); + $created[] = $type; + } + } + + if ([] !== $created) { + $this->em->flush(); + } + + return $created; + } + + /** + * @return list + */ + public function getAll(): array + { + return $this->repository->findAll(); + } + + public function getByType(string $type): ?PriceAutomatic + { + return $this->repository->findOneBy(['type' => $type]); + } + + /** + * @return array + */ + public static function getDefaultTypes(): array + { + return self::DEFAULT_PRICES; + } +} diff --git a/templates/admin/_layout.html.twig b/templates/admin/_layout.html.twig index d0074f9..ec327a7 100644 --- a/templates/admin/_layout.html.twig +++ b/templates/admin/_layout.html.twig @@ -81,6 +81,10 @@ Numerotation + + + Tarification + {% endif %} diff --git a/templates/admin/tarification/index.html.twig b/templates/admin/tarification/index.html.twig new file mode 100644 index 0000000..f6b3398 --- /dev/null +++ b/templates/admin/tarification/index.html.twig @@ -0,0 +1,75 @@ +{% extends 'admin/_layout.html.twig' %} + +{% block title %}Tarification - Administration - CRM SITECONSEIL{% endblock %} + +{% block admin_content %} +
+

Tarification

+ + {% for type, messages in app.flashes %} + {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} + {% endfor %} + +
+ {% for price in prices %} +
+
+
+ {{ price.title }} + {{ price.type }} +
+
+ {{ price.priceHt }} € HT + {% if price.monthPrice != '0.00' %} + + {{ price.monthPrice }} €/mois + {% endif %} +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ {% endfor %} +
+
+{% endblock %}