feat(src/Entity/CustomerAdvertPayment.php): Ajoute relation one-to-one avec AvisPaymentState
 feat(src/Controller): Crée ValidateAdvertController et template pour validation des avis
 feat(translations): Ajoute traduction pour renouvellement nom de domaine
 feat(templates): Ajoute lien vers validation des avis de paiement dans intranet
 feat(templates): Améliore formulaire prix avec champs dépot, renouvellement, rétablissement et gestion NDD
 feat(src/Command): Crée commande pour générer automatiquement les avis de paiement
 feat(src/Controller): Ajoute les custom price pour dépot, renouvellement, retablissement, gestion NDD
 feat(src/Repository): Ajoute une fonction pour recuperer les ndd expirant bientot
 feat(src/Controller): Affiche seulement les avis valider dans les factures client
 feat(src/Service): Ajoute fonction pour recuperer les ndd expirant bientot
```
This commit is contained in:
Serreau Jovann
2025-11-05 12:33:58 +01:00
parent 13b25efb41
commit 616bb107d3
16 changed files with 863 additions and 65 deletions

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20251105103144 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE avis_payment_state (id SERIAL NOT NULL, avis_payment_id INT DEFAULT NULL, is_generated BOOLEAN NOT NULL, is_validated BOOLEAN NOT NULL, is_first_send BOOLEAN NOT NULL, is_second_send BOOLEAN NOT NULL, is_third_send BOOLEAN NOT NULL, is_final_send BOOLEAN NOT NULL, type VARCHAR(255) NOT NULL, target VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE UNIQUE INDEX UNIQ_D52826ABE8060482 ON avis_payment_state (avis_payment_id)');
$this->addSql('ALTER TABLE avis_payment_state ADD CONSTRAINT FK_D52826ABE8060482 FOREIGN KEY (avis_payment_id) REFERENCES customer_advert_payment (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('ALTER TABLE avis_payment_state DROP CONSTRAINT FK_D52826ABE8060482');
$this->addSql('DROP TABLE avis_payment_state');
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20251105105917 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE avis_payment_state ADD first_send_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL');
$this->addSql('ALTER TABLE avis_payment_state ADD second_send_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL');
$this->addSql('ALTER TABLE avis_payment_state ADD third_send_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL');
$this->addSql('ALTER TABLE avis_payment_state ADD final_send_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL');
$this->addSql('COMMENT ON COLUMN avis_payment_state.first_send_at IS \'(DC2Type:datetime_immutable)\'');
$this->addSql('COMMENT ON COLUMN avis_payment_state.second_send_at IS \'(DC2Type:datetime_immutable)\'');
$this->addSql('COMMENT ON COLUMN avis_payment_state.third_send_at IS \'(DC2Type:datetime_immutable)\'');
$this->addSql('COMMENT ON COLUMN avis_payment_state.final_send_at IS \'(DC2Type:datetime_immutable)\'');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('ALTER TABLE avis_payment_state DROP first_send_at');
$this->addSql('ALTER TABLE avis_payment_state DROP second_send_at');
$this->addSql('ALTER TABLE avis_payment_state DROP third_send_at');
$this->addSql('ALTER TABLE avis_payment_state DROP final_send_at');
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20251105105927 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
}
}

View File

@@ -0,0 +1,143 @@
<?php
namespace App\Command;
use App\Entity\AvisPaymentState;
use App\Entity\CustomerAdvertPayment;
use App\Entity\CustomerAdvertPaymentLine;
use App\Entity\CustomerDns;
use App\Repository\AvisPaymentStateRepository;
use App\Repository\CustomerAdvertPaymentRepository;
use App\Repository\CustomerDnsRepository;
use App\Repository\CustomerPriceRepository;
use App\Service\Customer\CreateAvisEvent;
use App\Service\Ovh\Client;
use Doctrine\ORM\EntityManagerInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(name: 'mainframe:auto:avispayment')]
class AutoCreatedAvisPaymentCommand extends Command
{
public function __construct(
private Client $ovhClient,
private CustomerDnsRepository $customerDnsRepository,
private CustomerAdvertPaymentRepository $customerAdvertPaymentRepository,
private AvisPaymentStateRepository $avisPaymentStateRepository,
private EntityManagerInterface $entityManager,
private CustomerPriceRepository $customerPriceRepository,
private EventDispatcherInterface $eventDispatcher,
?string $name = null)
{
parent::__construct($name);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title("Auto Generated Avis Payment");
$nddRenouvellement = $this->customerPriceRepository->findOneBy(['type'=>'renouvellement_ndd']);
$nddGestion = $this->customerPriceRepository->findOneBy(['type'=>'gestion_ndd']);
/** @var CustomerDns $value */
foreach ($this->customerDnsRepository->expitedSoon() as $value) {
$searchCurrentStat = $this->avisPaymentStateRepository->findOneBy(['type'=>'ndd','target'=>$value->getNdd()]);
if(!$searchCurrentStat){
$t = new \DateTimeImmutable();
$num = "A-".$t->format('Y/m')."/".sprintf('%05d',$this->customerAdvertPaymentRepository->count()+1);
$io->info("Generate avis payment - ".$value->getNdd());
$avisPayment = new CustomerAdvertPayment();
$avisPayment->setState("created");
$avisPayment->setCustomer($value->getCustomer());
$avisPayment->setCreateAt(new \DateTimeImmutable('now'));
$avisPayment->setUpdateAt(new \DateTimeImmutable('now'));
$avisPayment->setNumAvis($num);
$this->entityManager->persist($avisPayment);
$expiredAt = $value->getExpiredAt();
$newLimitAt = clone $value->getExpiredAt();
$newLimitAt->modify("+1 year");
$desciption = $nddRenouvellement->getDescription();
$content = explode("\n", $desciption);
$title = $content[0];
unset($content[0]);
$desciption = implode("\n", $content);
$desciption = str_replace("{ndd}",$value->getNdd(),$desciption);
$desciption = str_replace("{date_start}",$expiredAt->format('M Y'),$desciption);
$desciption = str_replace("{date_stop}",$newLimitAt->format('M Y'),$desciption);
$avisLine = new CustomerAdvertPaymentLine();
$avisLine->setPos(0);
$avisLine->setName($title);
$avisLine->setPriceHT($nddRenouvellement->getAmmount());
$avisLine->setContent($desciption);
$avisLine->setTva(1.20);
$this->entityManager->persist($avisLine);
$avisPayment->addCustomerAdvertPaymentLine($avisLine);
$desciption = $nddGestion->getDescription();
$content = explode("\n", $desciption);
$title = $content[0];
unset($content[0]);
$desciption = implode("\n", $content);
$desciption = str_replace("{ndd}",$value->getNdd(),$desciption);
$desciption = str_replace("{date_start}",$expiredAt->format('M Y'),$desciption);
$desciption = str_replace("{date_stop}",$newLimitAt->format('M Y'),$desciption);
$avisLine2 = new CustomerAdvertPaymentLine();
$avisLine2->setPos(1);
$avisLine2->setName($title);
$avisLine2->setPriceHT($nddGestion->getAmmount());
$avisLine2->setContent($desciption);
$avisLine2->setTva(1.20);
$this->entityManager->persist($avisLine2);
$avisPayment->addCustomerAdvertPaymentLine($avisLine2);
$this->entityManager->persist($avisPayment);
$tFirst = new \DateTimeImmutable();
$tSecond = new \DateTimeImmutable();
$tSecond = $tSecond->modify("+1 month");
$tThird = new \DateTimeImmutable();
$tThird = $tThird->modify("+1 month");
$tThird = $tThird->modify("+2 week");
$tFinal = new \DateTimeImmutable();
$tFinal = $tFinal->modify("+1 month");
$tThird = $tFinal->modify("+2 week");
$tFinal = $tFinal->modify("+4 week");
$avisState = new AvisPaymentState();
$avisState->setAvisPayment($avisPayment);
$avisState->setFirstSendAt($tFirst);
$avisState->setSecondSendAt($tSecond);
$avisState->setThirdSendAt($tThird);
$avisState->setFinalSendAt($tFinal);
$avisState->setType("ndd");
$avisState->setTarget($value->getNdd());
$avisState->setIsGenerated(true);
$avisState->setIsValidated(false);
$avisState->setIsFirstSend(false);
$avisState->setIsSecondSend(false);
$avisState->setIsThirdSend(false);
$avisState->setIsFinalSend(false);
$this->entityManager->persist($avisState);
$this->entityManager->flush();
$event = new CreateAvisEvent($avisPayment, false);
$this->eventDispatcher->dispatch($event);
$io->info("Completed avis payement generated");
} else {
$io->info("Skip avis payment is already generated");
}
}
return Command::SUCCESS;
}
}

View File

@@ -6,6 +6,7 @@ use App\Service\Echeance\EventEcheanceCreated;
use App\Service\Pdf\EmailListPdf;
use App\Service\Pdf\PaymentPdf;
use App\Entity\{Account,
AvisPaymentState,
Customer,
CustomerAdvertPayment,
CustomerAdvertPaymentLine,
@@ -625,8 +626,18 @@ class CustomerController extends AbstractController
$splitList = $entityManager->getRepository(CustomerSplit::class)->findBy(['customer' => $customer], ['id' => 'ASC']);
$orderDevis = $entityManager->getRepository(CustomerDevis::class)->findBy(['customer' => $customer], ['id' => 'ASC']);
$orderAdvert = $entityManager->getRepository(CustomerAdvertPayment::class)->findBy(['customer' => $customer], ['id' => 'ASC']);
$orderAdvertTemp = $entityManager->getRepository(CustomerAdvertPayment::class)->findBy(['customer' => $customer], ['id' => 'ASC']);
$orderOrder = $entityManager->getRepository(CustomerOrder::class)->findBy(['customer' => $customer], ['id' => 'ASC']);
$orderAdvert =[];
foreach ($orderAdvertTemp as $value) {
if($value->getAvisPaymentState() instanceof AvisPaymentState){
if($value->getAvisPaymentState()->isValidated()) {
$orderAdvert[] = $value;
}
} else {
$orderAdvert[] = $value;
}
}
$nddEmails = [];
if ($request->query->has('idNdd')) {

View File

@@ -69,35 +69,64 @@ class PriceController extends AbstractController
FormFactoryInterface $formFactory,
)
{
$loggerService->log("VIEW","Affichage price automatique",$this->getUser());
$pricesList = [
'price_ndd',
'price_hosting',
'price_email'
];
foreach ($pricesList as $price) {
$ptc = $customerPriceRepository->findOneBy(['type' => $price]);
if(!$ptc instanceof CustomerPrice) {
$ptc = new CustomerPrice();
$ptc->setType($price);
$ptc->setAmmount(0);
$ptc->setDescription("");
$entityManager->persist($ptc);
if($request->isMethod('POST')) {
$data = $request->request->all();
if(isset($data['price_ndd_depot'])) {
$customPrice = $customerPriceRepository->findOneBy(['type'=>'depot_ndd']);
if(!$customPrice) {
$customPrice = new CustomerPrice();
$customPrice->setType('depot_ndd');
}
$customPrice->setDescription($data['description_ndd_depot']);
$customPrice->setAmmount(floatval($data['price_ndd_depot']));
$entityManager->persist($customPrice);
$entityManager->flush();
return $this->redirectToRoute('artemis_intranet_price');
}
if(isset($data['price_ndd_renouvellement'])) {
$customPrice = $customerPriceRepository->findOneBy(['type'=>'renouvellement_ndd']);
if(!$customPrice) {
$customPrice = new CustomerPrice();
$customPrice->setType('renouvellement_ndd');
}
$customPrice->setDescription($data['description_ndd_renouvellement']);
$customPrice->setAmmount(floatval($data['price_ndd_renouvellement']));
$entityManager->persist($customPrice);
$entityManager->flush();
return $this->redirectToRoute('artemis_intranet_price');
}
if(isset($data['price_ndd_retablisement'])) {
$customPrice = $customerPriceRepository->findOneBy(['type'=>'restore_ndd']);
if(!$customPrice) {
$customPrice = new CustomerPrice();
$customPrice->setType('restore_ndd');
}
$customPrice->setDescription($data['description_ndd_retablisement']);
$customPrice->setAmmount(floatval($data['price_ndd_retablisement']));
$entityManager->persist($customPrice);
$entityManager->flush();
return $this->redirectToRoute('artemis_intranet_price');
}
if(isset($data['price_ndd_gestion'])) {
$customPrice = $customerPriceRepository->findOneBy(['type'=>'gestion_ndd']);
if(!$customPrice) {
$customPrice = new CustomerPrice();
$customPrice->setType('gestion_ndd');
}
$customPrice->setDescription($data['description_ndd_gestion']);
$customPrice->setAmmount(floatval($data['price_ndd_gestion']));
$entityManager->persist($customPrice);
$entityManager->flush();
return $this->redirectToRoute('artemis_intranet_price');
}
}
$forms = [];
foreach ($customerPriceRepository->findAll() as $price) {
$fc = $formFactory->createNamed('price_'.$price->getId(),PriceType::class,$price);
$fc->handleRequest($request);
$forms[$price->getType()] = $fc->createView();
}
return $this->render('artemis/intranet/price.twig', [
'ptcs' => $forms
'depot_ndd' => $customerPriceRepository->findOneBy(['type'=>'depot_ndd']),
'renouvellement_ndd' => $customerPriceRepository->findOneBy(['type'=>'renouvellement_ndd']),
'restore_ndd' => $customerPriceRepository->findOneBy(['type'=>'restore_ndd']),
'gestion_ndd' => $customerPriceRepository->findOneBy(['type'=>'gestion_ndd']),
]);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Controller\Artemis\Intranet;
use App\Entity\AvisPaymentState;
use App\Repository\AvisPaymentStateRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Attribute\Route;
class ValidateAdvertController extends AbstractController
{
#[Route(path: '/artemis/intranet/validateAdvert', name: 'artemis_intranet_validateAdvert', methods: ['GET', 'POST'])]
public function validateAdvert(AvisPaymentStateRepository $avisPaymentStateRepository)
{
/** @var AvisPaymentState[] $list */
$list = $avisPaymentStateRepository->findBy(['isValidated'=>false]);
return $this->render('artemis/intranet/validateAdvert.twig', [
"listing" => $list
]);
}
}

View File

@@ -0,0 +1,215 @@
<?php
namespace App\Entity;
use App\Repository\AvisPaymentStateRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: AvisPaymentStateRepository::class)]
class AvisPaymentState
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\OneToOne(inversedBy: 'avisPaymentState', cascade: ['persist', 'remove'])]
private ?CustomerAdvertPayment $avisPayment = null;
#[ORM\Column]
private ?bool $isGenerated = null;
#[ORM\Column]
private ?bool $isValidated = null;
#[ORM\Column]
private ?bool $isFirstSend = null;
#[ORM\Column]
private ?bool $isSecondSend = null;
#[ORM\Column]
private ?bool $isThirdSend = null;
#[ORM\Column]
private ?bool $isFinalSend = null;
#[ORM\Column(length: 255)]
private ?string $type = null;
#[ORM\Column(length: 255)]
private ?string $target = null;
#[ORM\Column]
private ?\DateTimeImmutable $firstSendAt = null;
#[ORM\Column]
private ?\DateTimeImmutable $secondSendAt = null;
#[ORM\Column]
private ?\DateTimeImmutable $thirdSendAt = null;
#[ORM\Column]
private ?\DateTimeImmutable $finalSendAt = null;
public function getId(): ?int
{
return $this->id;
}
public function getAvisPayment(): ?CustomerAdvertPayment
{
return $this->avisPayment;
}
public function setAvisPayment(?CustomerAdvertPayment $avisPayment): static
{
$this->avisPayment = $avisPayment;
return $this;
}
public function isGenerated(): ?bool
{
return $this->isGenerated;
}
public function setIsGenerated(bool $isGenerated): static
{
$this->isGenerated = $isGenerated;
return $this;
}
public function isValidated(): ?bool
{
return $this->isValidated;
}
public function setIsValidated(bool $isValidated): static
{
$this->isValidated = $isValidated;
return $this;
}
public function isFirstSend(): ?bool
{
return $this->isFirstSend;
}
public function setIsFirstSend(bool $isFirstSend): static
{
$this->isFirstSend = $isFirstSend;
return $this;
}
public function isSecondSend(): ?bool
{
return $this->isSecondSend;
}
public function setIsSecondSend(bool $isSecondSend): static
{
$this->isSecondSend = $isSecondSend;
return $this;
}
public function isThirdSend(): ?bool
{
return $this->isThirdSend;
}
public function setIsThirdSend(bool $isThirdSend): static
{
$this->isThirdSend = $isThirdSend;
return $this;
}
public function isFinalSend(): ?bool
{
return $this->isFinalSend;
}
public function setIsFinalSend(bool $isFinalSend): static
{
$this->isFinalSend = $isFinalSend;
return $this;
}
public function getType(): ?string
{
return $this->type;
}
public function setType(string $type): static
{
$this->type = $type;
return $this;
}
public function getTarget(): ?string
{
return $this->target;
}
public function setTarget(string $target): static
{
$this->target = $target;
return $this;
}
public function getFirstSendAt(): ?\DateTimeImmutable
{
return $this->firstSendAt;
}
public function setFirstSendAt(\DateTimeImmutable $firstSendAt): static
{
$this->firstSendAt = $firstSendAt;
return $this;
}
public function getSecondSendAt(): ?\DateTimeImmutable
{
return $this->secondSendAt;
}
public function setSecondSendAt(\DateTimeImmutable $secondSendAt): static
{
$this->secondSendAt = $secondSendAt;
return $this;
}
public function getThirdSendAt(): ?\DateTimeImmutable
{
return $this->thirdSendAt;
}
public function setThirdSendAt(\DateTimeImmutable $thirdSendAt): static
{
$this->thirdSendAt = $thirdSendAt;
return $this;
}
public function getFinalSendAt(): ?\DateTimeImmutable
{
return $this->finalSendAt;
}
public function setFinalSendAt(\DateTimeImmutable $finalSendAt): static
{
$this->finalSendAt = $finalSendAt;
return $this;
}
}

View File

@@ -76,6 +76,9 @@ class CustomerAdvertPayment
#[ORM\Column(length: 255, nullable: true)]
private ?string $typePayment = null;
#[ORM\OneToOne(mappedBy: 'avisPayment', cascade: ['persist', 'remove'])]
private ?AvisPaymentState $avisPaymentState = null;
public function __construct()
{
$this->customerAdvertPaymentLines = new ArrayCollection();
@@ -391,4 +394,26 @@ class CustomerAdvertPayment
return $this;
}
public function getAvisPaymentState(): ?AvisPaymentState
{
return $this->avisPaymentState;
}
public function setAvisPaymentState(?AvisPaymentState $avisPaymentState): static
{
// unset the owning side of the relation if necessary
if ($avisPaymentState === null && $this->avisPaymentState !== null) {
$this->avisPaymentState->setAvisPayment(null);
}
// set the owning side of the relation if necessary
if ($avisPaymentState !== null && $avisPaymentState->getAvisPayment() !== $this) {
$avisPaymentState->setAvisPayment($this);
}
$this->avisPaymentState = $avisPaymentState;
return $this;
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Repository;
use App\Entity\AvisPaymentState;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<AvisPaymentState>
*/
class AvisPaymentStateRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, AvisPaymentState::class);
}
// /**
// * @return AvisPaymentState[] Returns an array of AvisPaymentState objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('a')
// ->andWhere('a.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('a.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?AvisPaymentState
// {
// return $this->createQueryBuilder('a')
// ->andWhere('a.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

View File

@@ -40,4 +40,21 @@ class CustomerDnsRepository extends ServiceEntityRepository
// ->getOneOrNullResult()
// ;
// }
public function expitedSoon()
{
// Get the first day of the current month
$startOfCurrentMonth = new \DateTimeImmutable('first day of this month 00:00:00');
// Get the first day of the month after the next one
// e.g., if current month is Nov, this gets Jan 1st of the next year.
$startOfSecondNextMonth = $startOfCurrentMonth->modify('+2 months');
return $this->createQueryBuilder('c')
->andWhere('c.expiredAt >= :start')
->andWhere('c.expiredAt < :end')
->setParameter('start', $startOfCurrentMonth)
->setParameter('end', $startOfSecondNextMonth)
->getQuery()
->getResult();
}
}

View File

@@ -45,4 +45,10 @@ class Client
return null;
}
}
public function nddExpired()
{
$domain = $this->ovhClient->get('/domain');
dd($domain);
}
}

View File

@@ -122,6 +122,11 @@
</svg>
</button>
<ul id="submenu-intranet" class="submenu ml-6 mt-2 space-y-2">
<li>
<a href="{{ path('artemis_intranet_validateAdvert') }}" class="flex items-center p-2 text-base font-normal text-gray-900 dark:text-white {% if app.request.get('_route') == 'artemis_intranet_validateAdvert' %}bg-gray-200 dark:bg-gray-700{% endif %} rounded-lg">
<span class="ml-3">Avis de paiement en attends de validation</span>
</a>
</li>
<li>
<a href="{{ path('artemis_intranet_customer') }}" class="flex items-center p-2 text-base font-normal text-gray-900 dark:text-white {% if app.request.get('_route') == 'artemis_intranet_customer' %}bg-gray-200 dark:bg-gray-700{% endif %} rounded-lg">
<span class="ml-3">Client(s)</span>

View File

@@ -1,52 +1,165 @@
{% extends 'artemis/base.twig' %}
{% block content %}
{# Conteneur principal en mode sombre #}
<div class="bg-gray-900 min-h-screen text-gray-100 p-4 sm:p-6 lg:p-8">
<div class="container mx-auto">
{# Titre en couleur claire #}
<h1 class="text-3xl font-bold mb-6 text-white">Prix automatique</h1>
<div class=" mx-auto grid grid-cols-1 lg:grid-cols-2 gap-8">
<div class="bg-white dark:bg-gray-800 p-6 md:p-8 rounded-xl shadow-2xl transition duration-300">
<h2 class="text-1xl font-bold text-indigo-700 dark:text-indigo-400 mb-4">Dépot de nom de domaine</h2>
{# Le conteneur principal pour les formulaires #}
<div class="space-y-6">
{% for key,form in ptcs %}
{# Conteneur pour chaque formulaire : une carte plus claire sur fond sombre #}
<div class="bg-gray-800 p-6 rounded-lg shadow-xl border border-gray-700 transition duration-300 hover:shadow-2xl">
<h2 class="text-2xl font-bold mb-6 text-white">{{ key|trans }}</h2>
<form action="#" method="POST" class="space-y-6">
{{ form_start(form, {'attr': {'class': 'space-y-4'}}) }}
{# Style pour le champ 'ammount' #}
<div class="mb-4">
{# Label en blanc #}
<label for="{{ form.ammount.vars.id }}" class="block text-sm font-medium text-gray-200">Montant</label>
{# Champ de formulaire adapté au mode sombre (fond sombre, texte blanc) #}
{{ form_widget(form.ammount, {'attr': {'class': 'mt-1 block w-full px-3 py-2 border border-gray-700 bg-gray-900 text-white rounded-md shadow-sm placeholder-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm'}}) }}
{{ form_errors(form.ammount) }}
</div>
{# Style pour le champ 'description' #}
<div class="mb-4">
{# Label en blanc #}
<label for="{{ form.description.vars.id }}" class="block text-sm font-medium text-gray-200">Description</label>
{# Champ de formulaire adapté au mode sombre (fond sombre, texte blanc) #}
{{ form_widget(form.description, {'attr': {'class': 'mt-1 block w-full px-3 py-2 border border-gray-700 bg-gray-900 text-white rounded-md shadow-sm placeholder-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm'}}) }}
{{ form_errors(form.description) }}
</div>
{# Style pour le bouton d'envoi (inchangé car les couleurs Indigo fonctionnent bien en mode sombre) #}
<div class="text-right">
<button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 focus:ring-offset-gray-800">
Enregistrer
</button>
</div>
{{ form_end(form) }}
<!-- Champ Prix -->
<div>
<label for="price_ndd_depot" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Prix (€)</label>
<div class="relative mt-1 rounded-md shadow-sm">
<input type="number" name="price_ndd_depot" id="price_ndd_depot" value="{% if depot_ndd is not null %}{{ depot_ndd.ammount }}{% endif %}" step="0.01" required
class="block w-full rounded-lg border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 pl-7 pr-12 py-2 text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
placeholder="0.00">
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<span class="text-gray-500 dark:text-gray-400 sm:text-sm">€</span>
</div>
{% endfor %}
</div>
</div>
</div>
<!-- Champ Description -->
<div>
<label for="description_ndd_depot" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description</label>
<textarea id="description_ndd_depot" name="description_ndd_depot" rows="3" required
class="block w-full rounded-lg border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 shadow-sm p-3 text-gray-900 dark:text-white focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
placeholder="">{% if depot_ndd is not null %}{{ depot_ndd.description }}{% endif %}</textarea>
</div>
<div class="row">
<span class="badge bg-indigo-500 rounded p-1">{ndd}</span>
<span class="badge bg-indigo-500 rounded p-1">{date_start}</span>
<span class="badge bg-indigo-500 rounded p-1">{date_stop}</span>
</div>
<!-- Bouton d'action -->
<button type="submit"
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition duration-150 ease-in-out">
Sauvegarder
</button>
</form>
</div>
<div class="bg-white dark:bg-gray-800 p-6 md:p-8 rounded-xl shadow-2xl transition duration-300">
<h2 class="text-1xl font-bold text-indigo-700 dark:text-indigo-400 mb-4">Rénouvellement de nom de domaine</h2>
<form action="#" method="POST" class="space-y-6">
<!-- Champ Prix -->
<div>
<label for="price_ndd_renouvellement" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Prix (€)</label>
<div class="relative mt-1 rounded-md shadow-sm">
<input type="number" name="price_ndd_renouvellement" id="price_ndd_renouvellement" value="{% if renouvellement_ndd is not null %}{{ renouvellement_ndd.ammount }}{% endif %}" step="0.01" required
class="block w-full rounded-lg border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 pl-7 pr-12 py-2 text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
placeholder="0.00">
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<span class="text-gray-500 dark:text-gray-400 sm:text-sm">€</span>
</div>
</div>
</div>
<!-- Champ Description -->
<div>
<label for="description_ndd_renouvellement" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description</label>
<textarea id="description_ndd_renouvellement" name="description_ndd_renouvellement" rows="3" required
class="block w-full rounded-lg border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 shadow-sm p-3 text-gray-900 dark:text-white focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
placeholder="">{% if renouvellement_ndd is not null %}{{ renouvellement_ndd.description }}{% endif %}</textarea>
</div>
<div class="row">
<span class="badge bg-indigo-500 rounded p-1">{ndd}</span>
<span class="badge bg-indigo-500 rounded p-1">{date_start}</span>
<span class="badge bg-indigo-500 rounded p-1">{date_stop}</span>
</div>
<!-- Bouton d'action -->
<button type="submit"
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition duration-150 ease-in-out">
Sauvegarder
</button>
</form>
</div>
<div class="bg-white dark:bg-gray-800 p-6 md:p-8 rounded-xl shadow-2xl transition duration-300">
<h2 class="text-1xl font-bold text-indigo-700 dark:text-indigo-400 mb-4">Rétablissement du nom de domaine</h2>
<form action="#" method="POST" class="space-y-6">
<!-- Champ Prix -->
<div>
<label for="price_ndd_retablisement" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Prix (€)</label>
<div class="relative mt-1 rounded-md shadow-sm">
<input type="number" name="price_ndd_retablisement" id="price_ndd_retablisement" value="{% if restore_ndd is not null %}{{ restore_ndd.ammount }}{% endif %}" step="0.01" required
class="block w-full rounded-lg border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 pl-7 pr-12 py-2 text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
placeholder="0.00">
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<span class="text-gray-500 dark:text-gray-400 sm:text-sm">€</span>
</div>
</div>
</div>
<!-- Champ Description -->
<div>
<label for="description_ndd_retablisement" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description</label>
<textarea id="description_ndd_retablisement" name="description_ndd_retablisement" rows="3" required
class="block w-full rounded-lg border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 shadow-sm p-3 text-gray-900 dark:text-white focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
placeholder="">{% if restore_ndd is not null %}{{ restore_ndd.description }}{% endif %}</textarea>
</div>
<div class="row">
<span class="badge bg-indigo-500 rounded p-1">{ndd}</span>
<span class="badge bg-indigo-500 rounded p-1">{date_start}</span>
<span class="badge bg-indigo-500 rounded p-1">{date_stop}</span>
</div>
<!-- Bouton d'action -->
<button type="submit"
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition duration-150 ease-in-out">
Sauvegarder
</button>
</form>
</div>
<div class="bg-white dark:bg-gray-800 p-6 md:p-8 rounded-xl shadow-2xl transition duration-300">
<h2 class="text-1xl font-bold text-indigo-700 dark:text-indigo-400 mb-4">Gestion nom de domaine</h2>
<form action="#" method="POST" class="space-y-6">
<!-- Champ Prix -->
<div>
<label for="price_ndd_gestion" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Prix (€)</label>
<div class="relative mt-1 rounded-md shadow-sm">
<input type="number" name="price_ndd_gestion" id="price_ndd_gestion" value="{% if gestion_ndd is not null %}{{ gestion_ndd.ammount }}{% endif %}" step="0.01" required
class="block w-full rounded-lg border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 pl-7 pr-12 py-2 text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
placeholder="0.00">
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<span class="text-gray-500 dark:text-gray-400 sm:text-sm">€</span>
</div>
</div>
</div>
<!-- Champ Description -->
<div>
<label for="description_ndd_gestion" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description</label>
<textarea id="description_ndd_gestion" name="description_ndd_gestion" rows="3" required
class="block w-full rounded-lg border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 shadow-sm p-3 text-gray-900 dark:text-white focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
placeholder="">{% if gestion_ndd is not null %}{{ gestion_ndd.description }}{% endif %}</textarea>
</div>
<div class="row">
<span class="badge bg-indigo-500 rounded p-1">{ndd}</span>
<span class="badge bg-indigo-500 rounded p-1">{date_start}</span>
<span class="badge bg-indigo-500 rounded p-1">{date_stop}</span>
</div>
<!-- Bouton d'action -->
<button type="submit"
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition duration-150 ease-in-out">
Sauvegarder
</button>
</form>
</div>
</div>
{% endblock %}
{% block title %}

View File

@@ -0,0 +1,59 @@
{% extends 'artemis/base.twig' %}
{% block title %}
Validation des avis de paiement
{% endblock %}
{% block content %}
<div class="flex justify-between items-center mb-6">
<h2 class="text-3xl font-semibold text-gray-800 dark:text-gray-200">Validation des avis de paiement</h2>
</div>
<div class="overflow-x-auto bg-gray-800 rounded-lg shadow">
<table class="min-w-full divide-y divide-gray-700">
<thead class="bg-gray-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">N°</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Client</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Type</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Date</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Montant</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Statut</th>
<th class="px-6 py-3 text-center text-xs font-medium uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody>
{% for orderAdvert in listing %}
<tr class="hover:bg-gray-700">
<td class="px-6 py-4">{{ orderAdvert.avisPayment.numAvis }}</td>
<td class="px-6 py-4">
<span>{{ orderAdvert.avisPayment.customer.raisonSocial }}</span><br/>
<span>{{ orderAdvert.avisPayment.customer.address }}</span><br/>
<span>{{ orderAdvert.avisPayment.customer.zipcode }} {{ orderAdvert.avisPayment.customer.city }}</span>
</td>
<td class="px-6 py-4">{{ ('vadvert-'~orderAdvert.type)|trans }}</td>
<td class="px-6 py-4">{{ orderAdvert.avisPayment.createAt.format('d/m/Y') }}</td>
<td class="px-6 py-4">{{ (orderAdvert.avisPayment|totalOrder)|format_currency('EUR') }} </td>
<td class="px-6 py-4">En attends de validation</td>
<td class="px-6 py-4 text-center">
<a target="_blank" href="{{ vich_uploader_asset(orderAdvert.avisPayment,'file') }}" class="block bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded">Téléchager</a>
<a href="{{ path('artemis_intranet_validateAdvert',{id:orderAdvert.id}) }}" class="block bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded">Valider l'avis de paiement</a>
</td>
</tr>
{% endfor %}
</tbody>
<tfoot class="bg-gray-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">N°</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Type</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Client</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Date</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Montant</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Statut</th>
<th class="px-6 py-3 text-center text-xs font-medium uppercase tracking-wider">Actions</th>
</tr>
</tfoot>
</table>
</div>
{% endblock %}

View File

@@ -82,3 +82,4 @@ dns_email: Nom de Domaine + Emails
esyWeb_created: Créer en attends de validation
esyWeb_validate: Validation - En Attends de déploiement
vadvert-ndd : Renouvellement nom de domaine