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>
458 lines
13 KiB
Plaintext
458 lines
13 KiB
Plaintext
{
|
|
"doctrine/deprecations": {
|
|
"version": "1.1",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "1.0",
|
|
"ref": "87424683adc81d7dc305eefec1fced883084aab9"
|
|
}
|
|
},
|
|
"doctrine/doctrine-bundle": {
|
|
"version": "3.2",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "3.0",
|
|
"ref": "18ee08e513ba0303fd09a01fc1c934870af06ffa"
|
|
},
|
|
"files": [
|
|
"config/packages/doctrine.yaml",
|
|
"src/Entity/.gitignore",
|
|
"src/Repository/.gitignore"
|
|
]
|
|
},
|
|
"doctrine/doctrine-migrations-bundle": {
|
|
"version": "4.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "3.1",
|
|
"ref": "1d01ec03c6ecbd67c3375c5478c9a423ae5d6a33"
|
|
},
|
|
"files": [
|
|
"config/packages/doctrine_migrations.yaml",
|
|
"migrations/.gitignore"
|
|
]
|
|
},
|
|
"endroid/qr-code-bundle": {
|
|
"version": "6.1.0"
|
|
},
|
|
"friendsofphp/php-cs-fixer": {
|
|
"version": "3.94",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "3.39",
|
|
"ref": "97aaf9026490db73b86c23d49e5774bc89d2b232"
|
|
},
|
|
"files": [
|
|
".php-cs-fixer.dist.php"
|
|
]
|
|
},
|
|
"knplabs/knp-paginator-bundle": {
|
|
"version": "v6.10.0"
|
|
},
|
|
"knpuniversity/oauth2-client-bundle": {
|
|
"version": "2.20",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes-contrib",
|
|
"branch": "main",
|
|
"version": "1.20",
|
|
"ref": "1ff300d8c030f55c99219cc55050b97a695af3f6"
|
|
},
|
|
"files": [
|
|
"config/packages/knpu_oauth2_client.yaml"
|
|
]
|
|
},
|
|
"league/flysystem-bundle": {
|
|
"version": "3.6",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "1.0",
|
|
"ref": "913dc3d7a5a1af0d2b044c5ac3a16e2f851d7380"
|
|
},
|
|
"files": [
|
|
"config/packages/flysystem.yaml",
|
|
"var/storage/.gitignore"
|
|
]
|
|
},
|
|
"liip/imagine-bundle": {
|
|
"version": "2.17",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes-contrib",
|
|
"branch": "main",
|
|
"version": "1.8",
|
|
"ref": "d1227d002b70d1a1f941d91845fcd7ac7fbfc929"
|
|
},
|
|
"files": [
|
|
"config/packages/liip_imagine.yaml",
|
|
"config/routes/liip_imagine.yaml"
|
|
]
|
|
},
|
|
"nelmio/security-bundle": {
|
|
"version": "3.9",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "2.4",
|
|
"ref": "71045833e4f882ad9de8c95fe47efb99a1eec2f7"
|
|
},
|
|
"files": [
|
|
"config/packages/nelmio_security.yaml"
|
|
]
|
|
},
|
|
"php-http/discovery": {
|
|
"version": "1.20",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "1.18",
|
|
"ref": "f45b5dd173a27873ab19f5e3180b2f661c21de02"
|
|
},
|
|
"files": [
|
|
"config/packages/http_discovery.yaml"
|
|
]
|
|
},
|
|
"phpstan/phpstan": {
|
|
"version": "2.1",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes-contrib",
|
|
"branch": "main",
|
|
"version": "1.0",
|
|
"ref": "5e490cc197fb6bb1ae22e5abbc531ddc633b6767"
|
|
},
|
|
"files": [
|
|
"phpstan.dist.neon"
|
|
]
|
|
},
|
|
"phpunit/phpunit": {
|
|
"version": "13.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "11.1",
|
|
"ref": "1117deb12541f35793eec9fff7494d7aa12283fc"
|
|
},
|
|
"files": [
|
|
".env.test",
|
|
"phpunit.dist.xml",
|
|
"tests/bootstrap.php",
|
|
"bin/phpunit"
|
|
]
|
|
},
|
|
"scheb/2fa-bundle": {
|
|
"version": "8.5",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "6.0",
|
|
"ref": "1e6f68089146853a790b5da9946fc5974f6fcd49"
|
|
},
|
|
"files": [
|
|
"config/packages/scheb_2fa.yaml",
|
|
"config/routes/scheb_2fa.yaml"
|
|
]
|
|
},
|
|
"spomky-labs/pwa-bundle": {
|
|
"version": "1.5",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes-contrib",
|
|
"branch": "main",
|
|
"version": "1.3",
|
|
"ref": "ac33d9498ab25856f16d1d7df9ad4b9b0cad066d"
|
|
},
|
|
"files": [
|
|
"config/packages/pwa.yaml"
|
|
]
|
|
},
|
|
"stripe/stripe-php": {
|
|
"version": "20.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes-contrib",
|
|
"branch": "main",
|
|
"version": "19.0",
|
|
"ref": "d6829c693e3927a8972c7671d74a1a5c505712b0"
|
|
},
|
|
"files": [
|
|
"config/packages/stripe.yaml"
|
|
]
|
|
},
|
|
"symfony/amazon-mailer": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "4.4",
|
|
"ref": "9648db3ecae5c8a6b1a5f74715d3907124348815"
|
|
}
|
|
},
|
|
"symfony/asset-mapper": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "6.4",
|
|
"ref": "5ad1308aa756d58f999ffbe1540d1189f5d7d14a"
|
|
},
|
|
"files": [
|
|
"assets/app.js",
|
|
"assets/styles/app.css",
|
|
"config/packages/asset_mapper.yaml",
|
|
"importmap.php"
|
|
]
|
|
},
|
|
"symfony/console": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "5.3",
|
|
"ref": "1781ff40d8a17d87cf53f8d4cf0c8346ed2bb461"
|
|
},
|
|
"files": [
|
|
"bin/console"
|
|
]
|
|
},
|
|
"symfony/debug-bundle": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "5.3",
|
|
"ref": "5aa8aa48234c8eb6dbdd7b3cd5d791485d2cec4b"
|
|
},
|
|
"files": [
|
|
"config/packages/debug.yaml"
|
|
]
|
|
},
|
|
"symfony/flex": {
|
|
"version": "2.10",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "2.4",
|
|
"ref": "52e9754527a15e2b79d9a610f98185a1fe46622a"
|
|
},
|
|
"files": [
|
|
".env",
|
|
".env.dev"
|
|
]
|
|
},
|
|
"symfony/form": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "7.2",
|
|
"ref": "7d86a6723f4a623f59e2bf966b6aad2fc461d36b"
|
|
},
|
|
"files": [
|
|
"config/packages/csrf.yaml"
|
|
]
|
|
},
|
|
"symfony/framework-bundle": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "7.4",
|
|
"ref": "09f6e081c763a206802674ce0cb34a022f0ffc6d"
|
|
},
|
|
"files": [
|
|
"config/packages/cache.yaml",
|
|
"config/packages/framework.yaml",
|
|
"config/preload.php",
|
|
"config/routes/framework.yaml",
|
|
"config/services.yaml",
|
|
"public/index.php",
|
|
"src/Controller/.gitignore",
|
|
"src/Kernel.php",
|
|
".editorconfig"
|
|
]
|
|
},
|
|
"symfony/mailer": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "4.3",
|
|
"ref": "09051cfde49476e3c12cd3a0e44289ace1c75a4f"
|
|
},
|
|
"files": [
|
|
"config/packages/mailer.yaml"
|
|
]
|
|
},
|
|
"symfony/maker-bundle": {
|
|
"version": "1.67",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "1.0",
|
|
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
|
|
}
|
|
},
|
|
"symfony/messenger": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "6.0",
|
|
"ref": "d8936e2e2230637ef97e5eecc0eea074eecae58b"
|
|
},
|
|
"files": [
|
|
"config/packages/messenger.yaml"
|
|
]
|
|
},
|
|
"symfony/monolog-bundle": {
|
|
"version": "4.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "3.7",
|
|
"ref": "1b9efb10c54cb51c713a9391c9300ff8bceda459"
|
|
},
|
|
"files": [
|
|
"config/packages/monolog.yaml"
|
|
]
|
|
},
|
|
"symfony/notifier": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "5.0",
|
|
"ref": "178877daf79d2dbd62129dd03612cb1a2cb407cc"
|
|
},
|
|
"files": [
|
|
"config/packages/notifier.yaml"
|
|
]
|
|
},
|
|
"symfony/property-info": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "7.3",
|
|
"ref": "dae70df71978ae9226ae915ffd5fad817f5ca1f7"
|
|
},
|
|
"files": [
|
|
"config/packages/property_info.yaml"
|
|
]
|
|
},
|
|
"symfony/routing": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "7.4",
|
|
"ref": "bc94c4fd86f393f3ab3947c18b830ea343e51ded"
|
|
},
|
|
"files": [
|
|
"config/packages/routing.yaml",
|
|
"config/routes.yaml"
|
|
]
|
|
},
|
|
"symfony/security-bundle": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "7.4",
|
|
"ref": "c42fee7802181cdd50f61b8622715829f5d2335c"
|
|
},
|
|
"files": [
|
|
"config/packages/security.yaml",
|
|
"config/routes/security.yaml"
|
|
]
|
|
},
|
|
"symfony/translation": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "6.3",
|
|
"ref": "620a1b84865ceb2ba304c8f8bf2a185fbf32a843"
|
|
},
|
|
"files": [
|
|
"config/packages/translation.yaml",
|
|
"translations/.gitignore"
|
|
]
|
|
},
|
|
"symfony/twig-bundle": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "6.4",
|
|
"ref": "f250159ebe99153d0c640a3e7742876fc7453f2c"
|
|
},
|
|
"files": [
|
|
"config/packages/twig.yaml",
|
|
"templates/base.html.twig"
|
|
]
|
|
},
|
|
"symfony/uid": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "7.0",
|
|
"ref": "0df5844274d871b37fc3816c57a768ffc60a43a5"
|
|
}
|
|
},
|
|
"symfony/validator": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "7.0",
|
|
"ref": "8c1c4e28d26a124b0bb273f537ca8ce443472bfd"
|
|
},
|
|
"files": [
|
|
"config/packages/validator.yaml"
|
|
]
|
|
},
|
|
"symfony/web-profiler-bundle": {
|
|
"version": "8.0",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "7.3",
|
|
"ref": "a363460c1b0b4a4d0242f2ce1a843ca0f6ac9026"
|
|
},
|
|
"files": [
|
|
"config/packages/web_profiler.yaml",
|
|
"config/routes/web_profiler.yaml"
|
|
]
|
|
},
|
|
"symfony/webapp-pack": {
|
|
"version": "1.4",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes",
|
|
"branch": "main",
|
|
"version": "1.0",
|
|
"ref": "b9e6cc8e7b6069d0e8a816665809a423864eb4dd"
|
|
},
|
|
"files": [
|
|
"config/packages/messenger.yaml"
|
|
]
|
|
},
|
|
"twig/extra-bundle": {
|
|
"version": "v3.24.0"
|
|
},
|
|
"vich/uploader-bundle": {
|
|
"version": "2.9",
|
|
"recipe": {
|
|
"repo": "github.com/symfony/recipes-contrib",
|
|
"branch": "main",
|
|
"version": "1.13",
|
|
"ref": "1b3064c2f6b255c2bc2f56461aaeb76b11e07e36"
|
|
},
|
|
"files": [
|
|
"config/packages/vich_uploader.yaml"
|
|
]
|
|
}
|
|
}
|