17 Commits

Author SHA1 Message Date
Serreau Jovann
9b8e49c550 feat: entite Contrat + CRUD admin + formulaire creation
Entite Contrat:
- email, raisonSociale, type (migration_siteconseil), state (draft/send/signed/cancelled)
- submissionId, submitterCompanyId, submitterCustomerId (DocuSeal)
- 3 PDFs Vich (unsigned, signed, audit)
- customer (ManyToOne nullable, lie apres signature)
- Reference CTR_XXXXX, getTypeLabel()

Controller admin /admin/contrats:
- index: liste des contrats avec statut
- create: email + raison sociale + type de contrat
- show: detail avec infos client, contrat, PDFs, actions
- cancel: annulation

Templates:
- index: tableau + modal creation (email, raison sociale, select type)
- show: 2 blocs (client + contrat), boutons PDF/signe/audit/annuler

Vich mappings: contrat_pdf, contrat_signed_pdf, contrat_audit_pdf

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 08:09:51 +02:00
Serreau Jovann
18daf096fa feat: systeme complet echeancier SEPA, E-Flex, attestations, avertissements clients
Echeancier - Webhooks DocuSeal:
- Webhook form.completed: telecharge PDF signe + audit, state SIGNED, prepare SEPA, notifie client + admin
- Webhook form.declined: state CANCELLED, notifie client + admin
- Reference EC_ECH_XXXXX affichee dans PDF, emails, pages client, admin
- Attestation fin de paiement auto via DocuSeal au completion

Echeancier - SEPA Direct Debit (remplace Subscriptions):
- Page /echeancier/setup-payment/{id}: formulaire IBAN Stripe Elements + mandat SEPA
- Confirmation SetupIntent -> stocke PaymentMethod -> state ACTIVE
- Commande cron app:echeancier:process-payments: preleve les echeances dues via PaymentIntent off_session
- Webhooks payment_intent.succeeded/failed: met a jour EcheancierLine, notifie client
- Regularisation CB via Stripe Checkout en cas d'echec prelevement
- Bouton "Forcer prelevement" par echeance dans admin
- Infos SEPA stockees (last4, bank_code, country) + affichees admin
- Page setup_payment_done quand SEPA deja configure
- Annulation auto apres 2 rejets + sync paiements vers Advert lie

Echeancier - Lien Advert:
- Champ advert (ManyToOne nullable) sur Echeancier
- Select "Avis lie" dans formulaire creation
- AdvertPayment cree a chaque echeance payee
- Advert passe en accepted quand echeancier completed

Comptabilite:
- Export echeanciers CSV/JSON/PDF/PDF signe dans /admin/comptabilite
- Colonnes: reference, client, creance, majoration, total, paye, restant, Stripe PI, avis lie

Stats:
- Case "Total impaye global" = factures impayees + echeances non payees
- Tableau echeanciers en cours avec restant du

Confiance client:
- Statut Confiant/Attention/Danger calcule dynamiquement
- Badge en haut a droite de la fiche client
- Integre warningLevel (1st=Attention, 2nd=Attention, last=Danger)
- Creation echeancier bloquee si Danger (template + controller)

Avertissements client (tab Controle, ROLE_ROOT):
- 3 niveaux: 1st, 2nd (procedure suspension preparee), last (48h)
- Motifs cochables: impayes, irrespect, hors horaires, services gratuits
- PDF signe DocuSeal pour chaque avertissement (ClientWarningPdf)
- PDF levee avertissement signe (ClientWarningResetPdf)
- Webhooks DocuSeal client_warning + client_warning_reset
- Barre progression 4 etapes dans admin
- Mentions legales: huis clos, contestation direction@e-cosplay.fr

Cloture compte:
- Bouton "Envoyer notification de cloture" apres dernier avertissement
- PDF signe DocuSeal (ClientClosurePdf): suppression 24h, recouvrement, commissaire justice, forces ordre
- Bouton "Suspendre le compte" (state suspended)
- Webhook DocuSeal client_closure: envoie PDF signe a client + admin + direction

Factures:
- Auto-generation PDF si absent lors de l'envoi
- Bouton "Envoyer" visible meme sans PDF pour factures payees

E-Flex (financement services):
- Entites EFlex + EFlexLine (reference E_FLEX_XXXXX)
- Methodes: SEPA, CB (Stripe Checkout), virement manuel
- PDF contrat avec 2 signatures DocuSeal (Company + Client)
- Controller admin CRUD + force payment + paiement manuel
- Pages client: verify, process, sign, signed, setup SEPA, paiement CB
- Webhook DocuSeal eflex: telecharge PDFs, prepare Stripe, notifie
- Webhooks Stripe payment_intent: gestion paiements E-Flex
- Cron traite aussi les E-Flex SEPA dans process-payments
- Tab E-Flex dans fiche client avec liste + modal creation
- Emails: signature, signed, verify_code, echeance_payee, echeance_echec

Attestations custom (ROLE_ROOT):
- Entite AttestationCustom avec items JSON + HMAC SHA-256
- Repeater dynamique pour ajouter elements a attester
- PDF avec phrase officielle "Je soussigne(e)..." + QR code verification
- Signature manuelle dans DocuSeal (redirection)
- Webhook attestation_custom: telecharge PDF signe + audit
- Page publique /attestation/verify/{id}/{hmac} avec validation HMAC
- Lien dans sidebar Super Admin

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 07:45:22 +02:00
Serreau Jovann
0f2712bb36 feat: echeancier de paiement (entites + controller + template + email)
Entites :
- Echeancier : customer, description, totalAmountHt, state (draft/send/
  signed/active/completed/cancelled/default), stripeSubscriptionId,
  stripePriceId, submitterCompanyId/CustomerId, 3 PDF Vich (unsigned/
  signed/audit), submissionId (DocuSeal)
- EcheancierLine : position, amount, scheduledAt, state (prepared/ok/ko),
  stripeInvoiceId, paidAt, failureReason

Controller EcheancierController :
- create : cree echeancier avec N echeances mensuelles (montant reparti)
- show : detail echeancier avec progression
- send : envoie email proposition au client
- cancel : annule echeancier + subscription Stripe
- activate : cree Stripe Subscription (price + subscription + cancel_at)

Templates :
- admin/echeancier/show.html.twig : detail avec resume, progression,
  tableau echeances, actions (envoyer/activer/annuler)
- admin/clients/show.html.twig : onglet echeancier avec liste + modal creation
- emails/echeancier_proposition.html.twig : email proposition avec detail

Vich mappings : echeancier_pdf, echeancier_signed_pdf, echeancier_audit_pdf

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:31:28 +02:00
Serreau Jovann
b1e4c772a4 fix: suppression workflow Discord + deprecation doctrine controller_resolver
- Suppression .gitea/workflows/discord-notify.yml (plus de notification Discord a chaque push)
- Suppression controller_resolver.auto_mapping deprecie dans les 2 fichiers doctrine.yaml

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 23:41:02 +02:00
Serreau Jovann
8b35e2b6d2 feat: comptabilite + prestataires + rapport financier + stats dynamiques
Comptabilite (Super Admin) :
- ComptabiliteController avec 7 exports CSV/JSON compatibles SAGE
  (journal ventes, grand livre, FEC, balance agee, reglements,
  commissions Stripe 1.5%+0.25E, couts services)
- Export PDF via ComptaPdf (FPDF) avec bloc legal pre-rempli,
  tableau pagine, champ signature DocuSeal
- Signature electronique DocuSeal + callback + envoi email signe
  avec template dedie (compta_export_signed.html.twig)
- Rapport financier public (RapportFinancierPdf) : recettes par
  service, depenses (Stripe, infra, prestataires), bilan excedent/deficit
- Codes comptables clients EC-XXXX (plus de 411xxx)

Prestataires (Super Admin) :
- Entite Prestataire (raisonSociale, siret, email, phone, adresse)
- Entite FacturePrestataire (numFacture, montantHt, montantTtc,
  year, month, isPaid, PDF via Vich)
- CRUD complet avec recherche SIRET via proxy API data.gouv.fr
- Commande cron app:reminder:factures-prestataire (5 du mois)
- Factures prestataires integrees dans export couts services
- Sidebar Super Admin : entree Prestataires + Comptabilite

Stats (/admin/stats) :
- Cout prestataire dynamique depuis FacturePrestataire
- Fusion Infra + Prestataire en "Cout de fonctionnement"
- Commission Stripe corrigee (1.5% + 0.25E par transaction)

Divers :
- DocuSealService::sendComptaForSignature() + getApi()
- Customer::generateCodeComptable() format EC-XXXX-XXXXX
- Protection double prefixe EC- a la creation client
- Bouton regenerer PDF cache quand advert state=accepted
- Modals sans script inline (data-modal-open/close dans app.js)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 23:39:31 +02:00
Serreau Jovann
95d33a9a6d feat: gestion complete Devis + Avis de paiement + DocuSeal signature + mails
Devis :
- Entity DevisLine (pos, title, description, priceHt) liee a Devis (OneToMany cascade/orphanRemoval)
- Champs ajoutes sur Devis : customer (ManyToOne), submissionId, state machine (created/send/accepted/refused/cancel), raisonMessage, totaux HT/TVA/TTC, updatedAt, setUpdatedAt public
- Relation Devis <-> Advert changee de ManyToOne a OneToOne nullable
- Vich Attribute (migration Annotation -> Attribute) pour unsignedPdf/signedPdf/auditPdf
- DevisController CRUD complet : create (form repeater lignes + boutons rapides TarificationService), edit, cancel (libere OrderNumber), generate-pdf, send, resend, create-advert, events
- DevisPdf (FPDF/FPDI) : header legacy (logo, num, date, client), body lignes, summary totaux, footer SITECONSEIL + pagination, champ signature DocuSeal sur page devis + derniere page CGV
- OrderNumberService : preview() et generate() reutilisent les OrderNumber non utilises (isUsed=false) en priorite
- OrderNumber::markAsUnused() ajoute

DocuSeal integration devis :
- DocuSealService : sendDevisForSignature (avec completed_redirect_url), resendDevisSignature (archive ancienne submission), getSubmitterSlug, downloadSignedDevis (sauvegarde via Vich UploadedFile test=true)
- WebhookDocuSealController : dispatch par doc_type devis/attestation, handleDevisEvent (form.completed -> STATE_ACCEPTED + download PDF signe/audit, form.declined -> STATE_REFUSED + raison)
- DocusealEvent entity pour tracer form.viewed/started/completed/declined en temps reel
- Page evenements admin /admin/devis/{id}/events avec badges et payload JSON

Signature client :
- DevisProcessController : page publique /devis/process/{id}/{hmac} securisee par HMAC, boutons Signer (redirect DocuSeal) / Refuser (motif optionnel)
- Pages confirmation : signed.html.twig (merci + recap) et refused.html.twig (confirmation refus + motif)
- Nelmio whitelist : signature.esy-web.dev + signature.siteconseil.fr

Avis de paiement :
- Entity AdvertLine (pos, title, description, priceHt) liee a Advert
- Advert refactorise : customer, state, totaux, raisonMessage, submissionId, advertFile (Vich mapping advert_pdf), lines collection, updatedAt
- AdvertController : generate-pdf, send (mail + PJ + lien paiement), resend (rappel), cancel (delie devis, libere OrderNumber), search Meilisearch
- AdvertPdf (FPDF/FPDI) : QR code Endroid pointant vers /order/{numOrder}, texte "Scannez pour payer"
- OrderPaymentController : page publique /order/{numOrder} avec detail prestations, totaux, options paiement (placeholder)
- Creation auto depuis devis signe : copie client, totaux, lignes, meme OrderNumber

Meilisearch :
- Index customer_devis et customer_advert avec searchable (numOrder, customerName, customerEmail, state) et filterable (customerId, state)
- CRUD indexation sur chaque action (create, edit, send, cancel, create-advert)
- Recherche AJAX dans tabs Devis et Avis avec debounce + dropdown glassmorphism
- Sync admin : boutons syncDevis / syncAdverts + compteurs dans /admin/sync

Emails :
- MailerService : VCF auto (fiche contact SARL SITECONSEIL) en PJ sur tous les mails, bloc HTML pieces jointes injecte automatiquement (exclut .asc/.p7z/smime) avec icone trombone + taille fichier
- Templates : devis_to_sign, devis_signed_client/admin (PJ signed+audit), devis_refused_client/admin, advert_send (PJ + bouton paiement), ndd_expiration
- TestMailCommand : option --force-dsn pour envoyer via un DSN SMTP specifique (test prod depuis dev)

Commande NDD :
- app:ndd:check : verifie expiration domaines <= 30j, envoie mail groupe a monitor@siteconseil.fr
- Cron quotidien 8h (docker + ansible)

Divers :
- Titles templates : CRM SITECONSEIL -> SARL SITECONSEIL (52 fichiers)
- VAULT_URL dev = https://kms.esy-web.dev (comme prod)
- app.js : initDevisLines (repeater + drag & drop), initTabSearch, toggle refus devis
- app.scss : styles drag & drop
- setasign/fpdi-fpdf installe pour fusion PDF
- 5 migrations Doctrine

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:44:35 +02:00
Serreau Jovann
f396b759f9 fix: corriger les 18 tests en echec apres le refactoring
tests/TestUserProvider.php (nouveau):
- Implementation de UserProviderInterface pour l'environnement test
- loadUserByIdentifier(), refreshUser(), supportsClass()
- Le service etait reference dans security.yaml when@test mais
  n'existait pas

config/services_test.yaml (nouveau):
- Enregistrement de App\Tests\TestUserProvider comme service public
  pour que le container test puisse le resoudre

tests/Controller/LegalControllerTest.php:
- Selecteurs CSS mis a jour: .border-red-600 remplace par .border-red-300
  et .border-green-600 par .border-green-300 (glassmorphism)

tests/Controller/Admin/AdminControllersTest.php:
- testSyncIndex(): ajout de PriceAutomaticRepository et
  StripeWebhookSecretRepository dans les arguments de
  SyncController::index() (4 arguments au lieu de 2)

tests/Controller/MainControllersTest.php:
- testForgotPasswordFullFlow(): sendEmail attendu 2 fois au lieu de 1
  (step 2 envoie le code, step 3 envoie la confirmation de changement)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 23:31:13 +02:00
Serreau Jovann
d3e76f00de fix: corriger HMAC des logs + PDF style attestation + pagination glassmorphism + purge logs
src/Entity/AppLog.php:
- createdAt initialise avec date('Y-m-d H:i:s') au lieu de
  new DateTimeImmutable() pour tronquer les microsecondes
  (PostgreSQL arrondit les microsecondes differemment de PHP,
  ce qui causait des HMAC invalides a la relecture)
- generateHmac(): format Y-m-d\TH:i:s sans microsecondes

templates/admin/logs/pdf.html.twig (reecrit):
- Meme style que les attestations RGPD (templates/pdf/rgpd_*.html.twig):
  banniere gold avec logo, doc-type badge indigo, titre italic uppercase,
  info-grid avec cellules bordure indigo, tableaux data avec header dark,
  bloc HMAC avec encadre vert/rouge, footer SARL SITECONSEIL
- Logo passe au template via base64

src/Controller/Admin/LogsController.php:
- pdf(): injection de kernel.project_dir, chargement du logo en base64
  et passage au template

src/Command/PurgeEmailTrackingCommand.php:
- Ajout de la purge des AppLog de plus de 90 jours (meme seuil
  que EmailTracking), affiche le nombre de logs supprimes

templates/components/pagination/glass.html.twig (nouveau):
- Template de pagination KnpPaginator style glassmorphism:
  boutons glass avec hover, page active en glass-gold,
  fleches precedent/suivant

config/packages/knp_paginator.yaml (nouveau):
- Configuration KnpPaginator pour utiliser le template glass

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 23:15:00 +02:00
Serreau Jovann
1c82da99f3 fix: utiliser /uploads/devis comme URL pour les PDFs de devis
src/Controller/DevisPdfController.php:
- Route changee de /devis/pdf/{id}/{type} vers /uploads/devis/{id}/{type}
  pour garder une URL coherente avec le dossier uploads

config/packages/vich_uploader.yaml:
- uri_prefix change de /devis/pdf vers /uploads/devis

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:35:11 +02:00
Serreau Jovann
88053611a4 feat: controller securise pour servir les PDFs de devis + stockage prive
src/Controller/DevisPdfController.php (nouveau):
- Route /devis/pdf/{id}/{type} avec type = unsigned|signed|audit
- Requiert ROLE_USER minimum
- checkAccess(): les ROLE_EMPLOYE ont toujours acces,
  pour les clients un TODO est prepare pour verifier que le
  client connecte est bien lie au devis (a implementer quand
  la relation Customer sera ajoutee sur Devis)
- Sert le fichier via BinaryFileResponse en inline (affichage
  dans le navigateur) avec nom de telechargement propre
  (ex: signed-04-2026-00001.pdf)

config/packages/vich_uploader.yaml:
- Mapping devis_pdf: stockage deplace de public/uploads/devis
  vers var/uploads/devis (hors du dossier public, inaccessible
  directement par URL)
- uri_prefix change en /devis/pdf (pointe vers le controller)

config/packages/security.yaml:
- Suppression de la regle access_control sur /uploads/devis
  (remplacee par le controller avec verification plus fine)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:34:54 +02:00
Serreau Jovann
09148b5b33 feat: ajout champs submitter + PDFs Vich sur Devis + protection uploads
src/Entity/Devis.php:
- submitterSiteconseilId (int nullable): ID du soumetteur cote SITECONSEIL
  dans DocuSeal apres signature
- submitterCustomerId (int nullable): ID du soumetteur cote client
  dans DocuSeal apres signature
- unsignedPdf (string nullable) + unsignedPdfFile (Vich): PDF non signe
- signedPdf (string nullable) + signedPdfFile (Vich): PDF signe
- auditPdf (string nullable) + auditPdfFile (Vich): certificat d'audit
- updatedAt (DateTimeImmutable nullable): mis a jour automatiquement
  a chaque upload de fichier via les setters *File()
- Annotation #[Vich\Uploadable] sur la classe
- Les 3 champs fichier utilisent le mapping 'devis_pdf'

config/packages/vich_uploader.yaml:
- Nouveau mapping devis_pdf: stockage dans public/uploads/devis
  avec SmartUniqueNamer pour eviter les collisions de noms

config/packages/security.yaml:
- Nouvelle regle access_control: /uploads/devis requiert ROLE_USER
  (empeche l'acces aux PDF de devis sans etre connecte)

migrations/Version20260402203334.php:
- Ajout colonnes submitter_siteconseil_id, submitter_customer_id,
  unsigned_pdf, signed_pdf, audit_pdf, updated_at sur la table devis

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:33:47 +02:00
Serreau Jovann
a9057374d4 fix: deplacer le pool dns_infra_cache dans le bon fichier cache.yaml
config/packages/cache.yaml:
- Ajout du pool dns_infra_cache avec adapter cache.app et TTL 3600s
- Le pool etait dans config/packages/packages/cache.yaml qui est
  surcharge par config/packages/cache.yaml, donc le service
  dns_infra_cache n'existait pas

config/packages/packages/cache.yaml:
- Suppression du pool dns_infra_cache (doublon)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:17:29 +02:00
Serreau Jovann
28b84f09d4 feat: cache DNS report + purge EmailTracking + crons mis a jour
src/Controller/DnsReportController.php:
- Injection du pool cache dns_infra_cache via #[Autowire]
- Les resultats des checks sont caches avec la cle dns_infra_check_{token}
  pendant 1 heure (3600s) pour eviter de rappeler toutes les APIs
  (Cloudflare, AWS SES, Mailcow, RDAP, dig) a chaque rechargement
- La date du rapport est stockee dans le cache au format ISO 8601

config/packages/packages/cache.yaml:
- Nouveau pool dns_infra_cache sur Redis avec default_lifetime 3600s

src/Command/PurgeEmailTrackingCommand.php (nouveau):
- Commande app:email-tracking:purge qui supprime les EmailTracking
  dont sentAt est anterieur au seuil (90 jours par defaut)
- Option --days pour changer la retention (ex: --days=30)
- Utilise une requete DQL DELETE pour performance

ansible/deploy.yml.disabled:
- Nouveau cron "crm-siteconseil email-tracking purge": tous les jours
  a 5h du matin, supprime les EmailTracking de plus de 90 jours

docker/cron/entrypoint.sh:
- Liste complete des taches cron mise a jour avec:
  - */5 min: expire-pending, infra:snapshot
  - */15 min: services:check
  - toutes les heures: monitor:messenger
  - toutes les 2h: dns:check
  - toutes les 6h: stripe:sync
  - 3h: meilisearch consistency
  - 4h: attestations clean
  - 5h: email-tracking purge
  - 6h: cloudflare clean

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:16:50 +02:00
Serreau Jovann
6fa970e60d refactor: rebrand project to CRM SITECONSEIL (SARL SITECONSEIL)
- Rename all references from E-Cosplay/Ecosplay to SITECONSEIL
- Update entity from Association to SARL SITECONSEIL (Siret: 418664058)
- Update address to 27 rue Le Serurier, 02100 Saint-Quentin
- Update emails: contact@siteconseil.fr, rgpd@siteconseil.fr
- Update hosting from GCP to OVHcloud (Roubaix, Gravelines, Strasbourg, Paris)
- Update legal pages: mentions legales, CGV, RGPD, conformite, hebergement, cookies, CGU
- Add tarifs page with tabs: Site Internet, E-Commerce, Nom de domaine, Esy-Mail, Esy-Mailer, Esy-Tchat, Esy-Meet, Esy-Defender
- Add Discord webhook notification workflow
- Disable deploy and sonarqube workflows
- Update OAuth Keycloak realm to master
- Update logo references to logo_facture.png
- Remove forced image sizing in Liip Imagine filters
- Update SonarQube project key and badge token
- Update tribunal competent to Saint-Quentin
- Move tarif tabs JS to app.js (CSP compliance)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:48:25 +02:00
Serreau Jovann
686de99909 init 2026-04-01 15:42:52 +02:00
Serreau Jovann
beb12d2b75 Add webapp packages 2026-03-30 18:52:03 +02:00
Serreau Jovann
c1485046af Add initial set of files 2026-03-30 18:51:57 +02:00