Apparence
Modèles de données
Les interfaces TypeScript qui décrivent toutes les entités persistées par Klapy. Source unique : klapy-crm/src/types/index.ts.
TL;DR
- Toutes les interfaces vivent dans un seul fichier :
klapy-crm/src/types/index.ts. Si tu modifies un modèle, c'est là. - Convention de nommage : champs Firestore en
snake_case, types TS enPascalCase, constantes enUPPER_SNAKE_CASE. - Pas de vrais
enumTypeScript : on utilise des objetsas const+ un type dérivé. Tree-shake friendly et compatible avec les valeurs Firestore (strings). - Timestamps Firestore, jamais
Date. AliasTimestampré-exporté depuisfirebase/firestore. - PII isolée :
UserProfileest dans une sous-collection (users/{uid}/profile/main), pas dansUserAccount. Ne jamais fusionner les deux. - Champs protégés sur
UserAccount:plan,subscription_status,stripe_customer_id. Le client ne peut pas les écrire (voir Règles de sécurité).
Vue d'ensemble
Toutes les entités sont scopées par utilisateur : aucune collection partagée entre comptes. Voir Base de données pour l'arbre Firestore complet.
Index des modèles
| Modèle | Description | Path Firestore |
|---|---|---|
UserAccount | Compte utilisateur, plan, consentement RGPD | users/{uid} |
UserProfile | PII et paramètres facturation | users/{uid}/profile/main |
Contact | Personne (prospect, client, partenaire) | users/{uid}/contacts/{id} |
Company | Société rattachable à un contact | users/{uid}/companies/{id} |
Opportunity | Carte du Kanban commercial | users/{uid}/opportunities/{id} |
Quote | Devis | users/{uid}/quotes/{id} |
Invoice | Facture | users/{uid}/invoices/{id} |
Task | Tâche à faire | users/{uid}/tasks/{id} |
CalendarEvent | Évènement du calendrier (tournage, rdv, etc.) | users/{uid}/events/{id} |
Activity | Log d'interaction (append-only) | users/{uid}/activities/{id} |
Conventions
Constantes typées (pseudo-enums)
ts
export const QUOTE_STATUS = {
BROUILLON: 'draft',
ENVOYE: 'sent',
ACCEPTE: 'accepted',
REFUSE: 'refused',
EXPIRE: 'expired'
} as const;
export type QuoteStatus = (typeof QUOTE_STATUS)[keyof typeof QUOTE_STATUS];À utiliser systématiquement comme suit :
ts
import { QUOTE_STATUS, type QuoteStatus } from '@/types';
const status: QuoteStatus = QUOTE_STATUS.BROUILLON; // preferred
const status: QuoteStatus = 'draft'; // works but magic stringTimestamps
Tous les champs date Firestore utilisent l'alias :
ts
export type Timestamp = import('firebase/firestore').Timestamp;Jamais Date, jamais string. Pour convertir à l'affichage : ts.toDate().
Sérialisation Cloud Functions
Quand un objet est passé à une Cloud Function via httpsCallable, les Timestamp Firestore sont sérialisés en { _seconds, _nanoseconds }. La fonction doit reconstruire un Timestamp côté serveur si elle veut comparer des dates.
Constantes (enums)
| Constante | Valeurs | Utilisé par |
|---|---|---|
CONTACT_TYPE | individual, company, prospect, client, partner | Contact.type |
OPPORTUNITY_STATUS | new, contacted, quote_sent, negotiating, won, lost | Opportunity.status (colonnes Kanban) |
QUOTE_STATUS | draft, sent, accepted, refused, expired | Quote.status |
INVOICE_STATUS | draft, sent, partially_paid, paid, overdue | Invoice.status |
TASK_PRIORITY | low, normal, high | Task.priority |
TASK_STATUS | to_do, in_progress, done | Task.status |
EVENT_TYPE | shoot, meeting, delivery | CalendarEvent.type |
ACTIVITY_TYPE | call, email, note, meeting | Activity.type |
PLAN | starter, pro | UserAccount.plan |
SUBSCRIPTION_STATUS | pending, active, past_due, canceled | UserAccount.subscription_status |
Source de vérité
Les valeurs de ces constantes sont dupliquées dans firestore.rules pour valider à l'écriture côté serveur. Toute modification doit se faire dans les deux fichiers, sans quoi un nouvel état sera rejeté à l'écriture.
Pièges & gotchas (transversaux)
À ne pas faire
- Mettre une PII dans
UserAccount. Toujours dansUserProfile. - Écrire un champ 🔒 protégé depuis le client (
plan,subscription_status,stripe_customer_id,created_at,number). Les rules le rejettent. - Renommer un champ Firestore sans migration. Les documents existants gardent l'ancien nom.
- Utiliser
Dateau lieu deTimestamp. Casse VueFire et la sérialisation Cloud Functions. - Ajouter un nouvel état d'enum sans mettre à jour
firestore.rulesen parallèle. Le nouvel état sera rejeté à l'écriture. - Supprimer une
Activity, unQuotevalidé ou uneInvoicevalidée. Audit et obligations légales.
Voir aussi
- Base de données : où chaque interface est persistée dans Firestore
- Règles de sécurité : champs protégés et validation à l'écriture
- Cloud Functions : fonctions qui mutent les champs protégés
- Code source :
klapy-crm/src/types/index.ts