Prompt open source — v1
Construis ton propre CRM
en un prompt
On ne partage plus du code en open source — on partage la spécification qui le produit. Choisis ta stack, décris tes besoins, copie le prompt, passe-le à ton agent IA.
Ce que tu vas obtenir
1. Choisis ta stack
2. Ajoute tes fonctionnalités (optionnel)
Décris les fonctionnalités supplémentaires que tu veux. Elles seront intégrées au prompt.
3. Copie le prompt
Colle ce prompt dans Claude Code, Codex, Cursor ou tout autre agent IA capable de modifier des fichiers.
Voir le prompt complet
# Construis un CRM complet
Tu es un développeur senior fullstack. Construis un CRM (Customer Relationship Management) complet et fonctionnel pour une TPE/PME de services (conseil, freelance, agence).
Le CRM doit couvrir tout le cycle commercial : prospection, négociation, devis, signature, contrat et suivi. Il sera utilisé quotidiennement par 1 à 5 personnes.
## Stack technique
- Backend API : PHP / Symfony
- Frontend : Next.js (React)
- Base de données : MongoDB
- Déploiement : Docker Compose
## Structure du projet
Monorepo avec :
```
apps/
api/ → PHP / Symfony
web/ → Next.js (React)
docker-compose.yml (ou equivalent Docker Compose)
README.md
```
Le backend et le frontend sont deux applications indépendantes qui communiquent via une API REST. Le frontend ne contient aucune logique métier — toute la validation, les calculs et les transitions d'état sont côté API.
## Modèle de données
Le CRM est structuré autour de 3 cycles de vie indépendants :
- **Cycle entreprise** : une entreprise est soit un prospect, soit un client (dérivé de ses deals/contrats)
- **Cycle deal** : une opportunité commerciale traverse le pipeline de vente
- **Cycle contrat** : un contrat signé a son propre suivi (actif → terminé → payé)
### Entités et relations
```
Entreprise 1──N Contact
1──N Deal 1──N Quote ──signature──▶ Contract
```
**Entreprise** (compagnie B2B) :
- `id` (string, auto-généré)
- `company` (string, requis) — nom de la société
- `address` (string) — adresse postale complète
- `color` (string) — code couleur hex pour la pastille visuelle (#FF1A81 par défaut)
- `tags` (string[]) — tags libres pour le filtrage
- `siret` (string) — numéro SIRET
- `sector` (string) — secteur d'activité
- `website` (string) — URL du site web
- `companyType` (string) — tpe, pme, startup, scaleup, eti, grand_groupe
- `foundedYear` (int), `founders` (string), `teamSize` (string), `headquarters` (string), `revenue` (string)
- `createdAt`, `updatedAt` (timestamps)
**Contact** (personne physique rattachée à une entreprise) :
- `id`, `firstName` (string), `lastName` (string), `email` (string, requis)
- `phone` (string), `jobTitle` (string) — fonction dans l'entreprise (CTO, CEO…)
- `linkedin` (string) — URL du profil LinkedIn
- `entrepriseId` (FK → Entreprise) — entreprise courante (modifiable si la personne change de boîte)
- `source` (string) — comment le contact a été trouvé (crm, prospection, lead-magnet…)
- `notes` (string) — texte libre
- Computed : `name` = `firstName + " " + lastName`
**Deal** (opportunité commerciale — un dossier dans le pipeline) :
- `id`, `entrepriseId` (FK → Entreprise, requis)
- `title` (string, requis) — ex: "Formation IA 2j", "Sprint MVP"
- `stage` (string) — une des valeurs : prospect, premier_contact, qualification, proposition, negociation, gagne, perdu, refuse
- `estimatedValue` (int, centimes) — valeur estimée du deal
- `expectedCloseDate` (string, YYYY-MM-DD) — date de closing espérée
- `lostReason` (string) — motif si le deal est perdu
- `contactId` (FK → Contact, optionnel) — interlocuteur principal
- Une entreprise peut avoir plusieurs deals en parallèle (ex: une formation + du conseil)
- Les devis et contrats référencent le deal via leur propre champ `dealId`, pas l'inverse
**Quote** (devis) :
- `id`, `number` (string, auto-généré, format Dyyyymm-NNNN, ex: D202604-0003)
- `entrepriseId` (FK → Entreprise), `dealId` (FK → Deal, optionnel)
- `signerId` (FK → Contact, requis) — contact qui recevra et signera le devis
- `billingContactId` (FK → Contact, optionnel) — contact facturation (défaut = signataire)
- `businessContactId` (FK → Contact, optionnel) — contact métier (défaut = signataire)
- `type` (string) — jour_homme, formation, sprint_fondateur, forfait
- `title` (string), `description` (string)
- `lineItems` (array) — chaque ligne : `{ title, description, quantity, unitPriceHt (centimes), vatRate (décimal, ex: 0.20) }`
- `discountPercentage` (float, 0-100), `discountLabel` (string)
- `paymentType` (string) — a_reception ou a_30_jours
- `termsAndConditions` (string) — conditions générales incluses dans le devis
- `issueDate` (string, YYYY-MM-DD), `expiryDate` (string, YYYY-MM-DD)
- `status` (string) — draft → sent → accepted → rejected → cancelled
- `acceptedBy` (string), `acceptedAt` (string) — infos de signature
- Computed : `totalHt` = somme(quantity × unitPriceHt), `totalHtAfterDiscount`, `totalVat` (TVA proportionnelle par ligne après remise), `totalTtc`
- Si pas de `dealId`, un Deal est auto-créé en stage "proposition" avec la valeur du devis
**Contract** (contrat = snapshot du devis signé + cycle d'exécution) :
- `id`, `entrepriseId` (FK), `dealId` (FK), `sourceQuoteId` (FK → Quote)
- `type` (string) — copié depuis le devis : jour_homme, formation, etc.
- `title`, `description`, `lineItems[]`, `discountPercentage`, `discountLabel`, `paymentType`, `termsAndConditions` — snapshot complet du devis au moment de la signature
- `signerId`, `billingContactId`, `businessContactId` — copiés depuis le devis
- `daysSold` (float) — somme des quantités des lignes du devis
- `dailyRate` (int, centimes) — premier unitPriceHt du devis (pour les jours homme)
- `totalAmount` (int, centimes, nullable) — pour les forfaits
- `monthlyAmount` (int, centimes, nullable) — pour les forfaits mensuels
- `startDate` (date, optionnel), `deadline` (date)
- `status` — active → completed → cancelled
- `completedAt`, `paidAt`, `signedAt` (timestamps)
- `primes` (array) — bonus facturés : `{ label, amountHT (centimes), date }`
## Fonctionnalités à implémenter
### 1. Authentification
- Inscription et connexion par email + mot de passe
- JWT avec TTL de 24h (ou session selon le framework)
- Middleware de protection des routes API — toutes les routes sous `/api/` sont protégées sauf les routes publiques
- Deux rôles : `admin` (accès complet) et `client` (accès restreint à ses propres données)
- Le rôle client ne voit que son entreprise, ses devis et ses contrats
### 2. Gestion des entreprises et contacts
- CRUD Entreprise avec tous les champs du modèle
- Recherche par nom (case-insensitive, partielle) — utilise un champ dénormalisé `companyLower` indexé
- Pastilles de couleur : à la création, assigner automatiquement la prochaine couleur disponible dans un palette de 10 couleurs
- CRUD Contact avec `firstName`, `lastName`, `jobTitle`
- Un contact appartient à une seule entreprise (modifiable)
- API : `GET /api/contacts?entrepriseId=xxx` pour lister les contacts d'une entreprise
- Pages : liste des entreprises (tableau filtrable par tags, recherche), fiche détail avec sections contacts, deals, contrats, devis
### 3. Pipeline commercial avec Deals
- Vue Kanban avec 8 colonnes (les stages du pipeline)
- Drag & drop pour changer le stage d'un deal — appel API `PATCH /api/deals/{id}/stage`
- Chaque carte affiche : titre, entreprise (pastille couleur + nom), valeur estimée en €, nombre de devis, indicateur contrat
- Les colonnes "perdu" et "refusé" sont repliables par défaut
- Wizard de création de deal en 3 étapes : 1) sélectionner ou créer une entreprise, 2) sélectionner ou créer un contact, 3) détails du deal (titre, valeur, date)
- Quand un deal passe à "gagné", c'est parce qu'un devis a été accepté (transition automatique)
- Quand tous les devis d'un deal sont rejetés et qu'il n'y a pas de contrat, le deal passe automatiquement à "perdu"
- Les deals enrichis par l'API incluent `quoteCount` et `hasContract` (calculés par query, pas de dénormalisation)
### 4. Devis
- Formulaire de création avec : sélecteur entreprise, sélecteur deal (deals ouverts de l'entreprise ou création auto), 3 sélecteurs contact (signataire obligatoire, métier optionnel, facturation optionnel), type de prestation, titre, description, lignes de devis dynamiques (ajouter/supprimer), remise, conditions de paiement, CGV
- Chaque sélecteur de contact propose les contacts de l'entreprise + un bouton "Créer un contact" qui ouvre un formulaire inline (prénom, nom, email)
- Templates de devis : l'admin crée des templates réutilisables (titre, description, lignes, conditions, type). Lors de la création d'un devis, un sélecteur de template pré-remplit le formulaire. Tout reste modifiable.
- Numérotation automatique par mois : D + yyyymm + "-" + numéro séquentiel paddé sur 4 chiffres
- Calculs temps réel dans le formulaire : total HT par ligne, remise, TVA proportionnelle, total TTC
- Transitions d'état côté API avec validations strictes : draft → sent (génère le PDF, envoie l'email), sent → accepted/rejected, draft → cancelled
- Page détail devis : infos client avec 3 blocs contact (signataire, métier, facturation), dates, lignes, totaux, conditions, boutons d'action selon le statut
- Bouton "Aperçu PDF" qui génère un PDF côté API et l'affiche dans le navigateur
- Page "Mes devis" côté client : affiche les devis du client avec statut "À signer" (pas "Envoyé") et "Signé" (pas "Accepté")
### 5. Contrats
- Créé automatiquement quand un devis est accepté — c'est un snapshot complet du devis (toutes les données sont copiées, pas de référence qui casse si le devis est modifié)
- Le deal associé passe automatiquement à "gagné"
- Tableau des contrats triés par date de signature décroissante, colonnes : Client, Titre, Signé le, Réalisé, Montant total, Statut
- Types avec calcul du montant total adapté : jour_homme (daysSold × dailyRate), forfait (totalAmount fixe), forfait_mensuel (monthlyAmount × nombre de mois)
- Workflow : active → completed (horodatage completedAt) → paid (horodatage paidAt, met aussi completedAt si pas déjà fait)
- Primes : tableau de bonus ponctuels (label + montant HT + date), affichés séparément du montant principal
- Page client "Mes prestations" : résumé du contrat actif (jours vendus/consommés/restants, TJM, barre de progression), message "Aucun contrat actif" si pas de contrat
### 6. Dashboard
- Dashboard admin :
- Nombre de deals par stage (avec pastilles couleur)
- Valeur totale du pipeline (somme estimatedValue des deals ouverts, en euros)
- CA mensuel (somme des contrats payés du mois en cours)
- Taux de conversion : devis envoyés vs acceptés sur les 3 derniers mois
- Dashboard client (rôle client) :
- Résumé du contrat actif : jours vendus, consommés, restants
- TJM (taux journalier moyen)
- Barre de progression visuelle
- Si aucun contrat actif : message explicatif (pas de 500)
### 7. Design et UX
- Utilise un design system existant (shadcn/ui pour Next.js, Vuetify pour Nuxt, Skeleton pour Svelte)
- Layout avec sidebar de navigation fixe : liens vers Dashboard, Pipeline, Entreprises, Contacts, Devis, Contrats
- Responsive : le sidebar se replie en menu hamburger sur mobile
- Couleurs professionnelles : fond neutre, accents bleu/cyan pour les actions, vert pour les succès, rouge pour les erreurs
- Toasts pour les actions réussies (sauvegarde, envoi, etc.)
- Loading states : spinners sur les chargements, skeleton screens sur les listes
- Formulaires : validation inline (champs requis en rouge), messages d'erreur sous chaque champ
- Modales de confirmation pour les actions destructives (supprimer, annuler, rejeter)
- Tableaux : tri par colonne, recherche, filtres par statut/client
- Badges de statut colorés : brouillon (gris), envoyé/à signer (bleu/ambre), accepté/signé (vert), rejeté (rouge), annulé (gris)
## Architecture et patterns
### Backend
- **Controllers légers** : les controllers ne contiennent que le parsing des requêtes, la validation des paramètres, et la délégation aux services. Aucune logique métier dans les controllers.
- **Services** : toute la logique métier (création, transitions d'état, calculs, effets de bord) est dans les services. Un service par entité principale.
- **Repository pattern** : chaque entité a un repository dédié qui encapsule les requêtes à la base de données. Les services ne font jamais de requêtes directes.
- **Validation** : valider côté API systématiquement. Rejeter avec un HTTP 400 et un message clair en JSON. Ne jamais faire confiance aux données du frontend.
- **Réponses API** : toujours retourner `{ id, ...fields }`. Pas de wrapper `{ data: ... }`. Les erreurs retournent `{ error: "message" }`.
- **Montants en centimes** : tous les montants sont stockés en centimes d'euros côté API. Le frontend convertit en euros pour l'affichage (`/ 100`) et reconvertit en centimes pour l'envoi (`Math.round(euros * 100)`).
- **Pas de dénormalisation** : les relations sont stockées par FK dans un seul sens. Pour trouver les devis d'un deal, on query `quotes.find({ dealId })`, on ne stocke pas un tableau `quoteIds[]` sur le deal.
### Frontend
- **Types TypeScript stricts** : une interface par entité, pas de `any`. Les types de l'API correspondent exactement aux réponses JSON.
- **Couche API centralisée** : un fichier `lib/api.ts` qui expose un objet par entité (`entreprisesApi`, `dealsApi`, `quotesApi`, etc.) avec des méthodes typées. Toutes les requêtes passent par une fonction `apiFetch()` qui gère le token JWT, les erreurs, et le parsing JSON.
- **Composants de formulaire réutilisables** : les sélecteurs de contact, d'entreprise, de deal sont des composants autonomes qui gèrent leur propre chargement de données et leur état.
- **Optimistic updates** : le drag & drop du Kanban met à jour l'UI immédiatement puis appelle l'API. En cas d'erreur, revert.
## Tests
### Backend
- **Test unitaire par service** : au minimum, tester le happy path de chaque méthode publique du service. Pour les services complexes (QuoteService, DealService), tester aussi les transitions d'état invalides.
- **Tests d'intégration** : tester les endpoints API critiques (création de devis, acceptation, envoi). Utiliser une base de données de test (in-memory ou base séparée).
- Cas de test obligatoires :
- Créer un devis → statut draft, numéro auto-généré, deal auto-créé
- Envoyer un devis → statut sent
- Accepter un devis → contrat créé, deal passe à gagné
- Rejeter le dernier devis d'un deal → deal passe à perdu
- Créer un contrat avec daysSold et dailyRate → totalContractValueHT calculé correctement
- Dupliquer un devis → nouveau numéro, statut draft, mêmes données
### Frontend
- **Au minimum des tests sur les fonctions de calcul** : totalHt, totalHtAfterDiscount, totalVat, totalTtc, discountAmount. Ces fonctions sont pures (pas de dépendance React) et faciles à tester.
### Analyse statique
- Linter configuré et propre (ESLint pour TS/JS, équivalent pour le backend)
- Aucune erreur de type TypeScript (`tsc --noEmit` passe)
- Formateur automatique (Prettier pour le frontend)
## CI/CD et déploiement
- `docker-compose.yml` (ou équivalent) qui lance : la base de données, l'API, le frontend
- Variables d'environnement : les secrets (JWT_SECRET, DB_PASSWORD) ne sont JAMAIS dans le code. Utiliser un fichier `.env` (gitignored) avec un `.env.example` qui liste les variables attendues.
- Script de démarrage : `docker compose up` doit suffire pour lancer le projet complet en local
- README.md avec : prérequis (Docker, Node), commandes de démarrage, structure du projet, variables d'environnement
- Optionnel mais bienvenu : GitHub Actions pour lancer les tests et le lint sur chaque push
## Livraison
Quand tu as terminé, fais un résumé structuré :
1. **Endpoints API** : tableau avec méthode, URL, description, authentification requise
2. **Pages frontend** : tableau avec route, description, rôle requis (admin/client/public)
3. **Modèle de données** : schéma des entités avec leurs champs et relations
4. **Démarrage** : commandes exactes pour lancer le projet en local depuis zéro
5. **Tests** : liste des tests écrits avec leur couverture
6. **Améliorations futures** : les 5 améliorations les plus utiles à implémenter ensuite
Ce que ce prompt ne fait pas
Ce prompt produit un starter impressionnant— mais pas un produit fini. Il ne structure pas la mémoire du projet, ne met pas en place d'audits automatiques, ne crée pas d'équipe d'experts qui challengent chaque décision. C'est la différence entre un one-shot et un process. Le process, c'est ce qu'on installe en formation.
Ce prompt t'a donné un CRM en 30 minutes ?
Imagine ce qu'un vrai process — avec mémoire du projet, experts auditeurs et itérations structurées — fait en 2 jours sur le workflow de toute ton équipe.
Prompt v1 — avril 2026. Basé sur le CRM développé par Rémi Alvado avec Claude Code. Le code est privé, la spécification est ouverte.