Cette documentation décrit la structure complète de la base de données PostgreSQL utilisée par l'application. Le schéma est géré via Doctrine ORM et comprend les entités pour la gestion des demandes de maintenance, des dépannages, des équipements, des offres et des contrats.
La base de données est organisée en plusieurs domaines métier :
erDiagram
ADMIN ||--o{ RESET_PASSWORD_REQUEST : has
ADMIN ||--o{ REFRESH_TOKEN : has
REQUEST ||--|| OFFER : has
REQUEST ||--|| ADDRESS_CONTACT : has
REQUEST ||--o| ADDRESS_BILLING : has
REQUEST ||--o{ EQUIPMENT : contains
REQUEST ||--o{ CONTRACT : generates
REQUEST ||--o| REFUSAL : may_have
REQUEST }o--|| DISCOUNT : may_have
OFFER ||--|| PRICE : has
OFFER }o--|| MAINTENANCE_OFFER : references
MAINTENANCE_OFFER ||--o{ FEATURE : has
EQUIPMENT }o--|| BRAND : references
EQUIPMENT }o--|| TYPE : references
TYPE }o--|| CATEGORY : belongs_to
CATEGORY ||--o{ COEFFICIENT_RATE : has
CONTRACT }o--|| REQUEST : belongs_to
REFUSAL ||--o{ REFUSAL_REASON : has
REFUSAL }o--|| REQUEST : belongs_to
ADDRESS }o--|| CIVILITY : references
TROUBLESHOOTING ||--|| ADDRESS : has
TROUBLESHOOTING ||--o{ DIAGNOSIS : has
ADMIN {
int id PK
string email UK
array roles
string password
}
REQUEST {
uuid id PK
datetime_immutable createdAt
datetime_immutable updatedAt
datetime_immutable expiredAt
string status
string paymentId
string paymentStatus
string paymentUrl
boolean lessThanTwoYears
datetime_immutable deletedAt
datetime_immutable archivedAt
}
OFFER {
uuid id PK
uuid request_id FK
int maintenanceOffer_id FK
string paymentType
}
PRICE {
int id PK
uuid offer_id FK
int ht
int htBase
int ttc
int ttcBase
int htTotal
int htTotalBase
int ttcTotal
int ttcTotalBase
boolean discount
array details
}
EQUIPMENT {
int id PK
uuid request_id FK
int brand_id FK
int type_id FK
date_immutable installedAt
int uiDuctable
int uiNotDuctable
}
CONTRACT {
uuid id PK
uuid request_id FK
string path
string signId
string signUrl
string status
datetime_immutable expiredAt
datetime_immutable createdAt
string savId
}
MAINTENANCE_OFFER {
int id PK
string name
string savLabel
int subscriptionDuration
int subscriptionVisit
text description
int loyaltyDiscount
float coefficient
string type
boolean best
int position
}
FEATURE {
int id PK
string label
}
CATEGORY {
int id PK
string name
string savLabel
}
TYPE {
int id PK
int category_id FK
string name
string savLabel
string section
float hourlyPrice
float hourRequire
float supplyPrice
float individualVAT
float professionalVAT
}
BRAND {
int id PK
string name
string savLabel
}
COEFFICIENT_RATE {
int id PK
int category_id FK
int minEquipment
float coefficient
}
ADDRESS {
int id PK
int civility_id FK
string name
string lastname
boolean isPro
string companyName
string siret
string vatNumber
string street
string additionalInfo
string postalCode
string city
string email
string phone
}
DISCOUNT {
int id PK
uuid request_id FK
string name
string code UK
string type
int amount
boolean enabled
datetime_immutable startAt
datetime_immutable endAt
}
REFUSAL {
uuid id PK
uuid request_id FK
text comment
datetime_immutable createdAt
}
REFUSAL_REASON {
int id PK
string name
}
TROUBLESHOOTING {
uuid id PK
int address_id FK
boolean hasContract
text details
array availability
text availabilityDetails
string status
text comments
datetime_immutable createdAt
datetime_immutable updatedAt
}
DIAGNOSIS {
int id PK
string name
}
CIVILITY {
int id PK
string name
string savLabel
}
POSTAL_CODE {
int id PK
string code UK
string label
float price
float diagnosisPrice
}
PARAMETER {
int id PK
string code UK
string label
string value
}
request (Maintenance\Request)Table principale pour les demandes de maintenance.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | uuid | PK, NOT NULL | Identifiant unique (UUID v6) |
created_at | datetime_immutable | NOT NULL | Date de création |
updated_at | datetime_immutable | NOT NULL | Date de mise à jour |
expired_at | datetime_immutable | NOT NULL | Date d'expiration (4 mois après création) |
status | enum | NOT NULL | Statut de la demande (PENDING, WAITING_SIGNATURE, WAITING_PAYMENT, COMPLETE, REJECTED) |
payment_id | varchar(255) | NULL | ID de paiement Stripe |
payment_status | enum | NULL | Statut du paiement |
payment_url | text | NULL | URL de paiement Stripe |
less_than_two_years | boolean | NOT NULL, DEFAULT false | Équipement de moins de 2 ans |
deleted_at | datetime_immutable | NULL | Date de suppression (soft delete) |
archived_at | datetime_immutable | NULL | Date d'archivage |
Relations :
OneToOne → offer (Maintenance\Offer)OneToOne → contact (Address)OneToOne → billing_info (Address)OneToMany → equipments (Maintenance\Equipment)OneToMany → contracts (Maintenance\Contract)OneToOne → refusal (Maintenance\Refusal)ManyToOne → discount (Maintenance\Discount)request_offer (Maintenance\Offer)Offres associées aux demandes.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | uuid | PK, NOT NULL | Identifiant unique |
request_id | uuid | FK, NOT NULL, UNIQUE | Référence à request |
maintenance_offer_id | int | FK, NOT NULL | Référence à maintenance_offer |
payment_type | enum | NOT NULL | Type de paiement (ONE_SHOT, SUBSCRIPTION) |
Relations :
ManyToOne → request (Maintenance\Request)ManyToOne → maintenanceOffer (MaintenanceOffer\MaintenanceOffer)OneToOne → price (Maintenance\Price)request_equipment (Maintenance\Equipment)Équipements associés à une demande.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
request_id | uuid | FK, NOT NULL | Référence à request |
brand_id | int | FK, NOT NULL | Référence à equipment_brand |
type_id | int | FK, NOT NULL | Référence à equipment_type |
installed_at | date_immutable | NULL | Date d'installation |
ui_ductable | int | NOT NULL, DEFAULT 0 | Nombre d'unités intérieures gainables |
ui_not_ductable | int | NOT NULL, DEFAULT 0 | Nombre d'unités intérieures non gainables |
Relations :
ManyToOne → request (Maintenance\Request)ManyToOne → brand (Equipment\Brand)ManyToOne → type (Equipment\Type)request_contract (Maintenance\Contract)Contrats générés pour les demandes.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | uuid | PK, NOT NULL | Identifiant unique |
request_id | uuid | FK, NOT NULL | Référence à request |
path | varchar(255) | NOT NULL | Chemin vers le PDF du contrat |
sign_id | varchar(255) | NULL | ID de signature Yousign |
sign_url | text | NULL | URL de signature Yousign |
status | enum | NOT NULL | Statut (PENDING, SIGNED, EXPIRED, CANCELLED) |
expired_at | datetime_immutable | NOT NULL | Date d'expiration (90 jours après création) |
created_at | datetime_immutable | NOT NULL | Date de création |
sav_id | varchar(255) | NULL | ID dans le système SAV |
Relations :
ManyToOne → request (Maintenance\Request)price (Maintenance\Price)Prix calculés pour les offres.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
offer_id | uuid | FK, NOT NULL, UNIQUE | Référence à request_offer |
ht | int | NOT NULL | Prix HT mensuel (en centimes) |
ht_base | int | NULL | Prix HT de base (avant réduction) |
ttc | int | NOT NULL | Prix TTC mensuel (en centimes) |
ttc_base | int | NULL | Prix TTC de base (avant réduction) |
ht_total | int | NOT NULL | Prix HT total (en centimes) |
ht_total_base | int | NULL | Prix HT total de base |
ttc_total | int | NOT NULL | Prix TTC total (en centimes) |
ttc_total_base | int | NULL | Prix TTC total de base |
discount | boolean | NOT NULL, DEFAULT false | Indique si une réduction est appliquée |
details | json | NULL | Détails du calcul du prix |
Relations :
OneToOne → offer (Maintenance\Offer)discount (Maintenance\Discount)Codes de réduction.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
name | varchar(255) | NOT NULL | Nom de la réduction |
code | varchar(255) | NOT NULL, UNIQUE | Code de réduction |
type | varchar(255) | NOT NULL | Type de réduction (percentage, fixed) |
amount | int | NOT NULL | Montant de la réduction |
enabled | boolean | NOT NULL | Actif ou non |
start_at | datetime_immutable | NOT NULL | Date de début |
end_at | datetime_immutable | NOT NULL | Date de fin |
Relations :
OneToMany → requests (Maintenance\Request)request_refusal (Maintenance\Refusal)Refus de demandes.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | uuid | PK, NOT NULL | Identifiant unique |
request_id | uuid | FK, NOT NULL, UNIQUE | Référence à request |
comment | text | NULL | Commentaire de refus |
created_at | datetime_immutable | NOT NULL | Date de création |
Relations :
OneToOne → request (Maintenance\Request)ManyToMany → reasons (RefusalReason) via table request_refusal_reasonequipment_category (Equipment\Category)Catégories d'équipements.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
name | varchar(255) | NOT NULL | Nom de la catégorie |
sav_label | varchar(255) | NOT NULL | Label pour le SAV |
Relations :
OneToMany → equipmentTypes (Equipment\Type)OneToMany → coefficientRates (CoefficientRate)equipment_type (Equipment\Type)Types d'équipements.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
category_id | int | FK, NOT NULL | Référence à equipment_category |
name | varchar(255) | NOT NULL | Nom du type |
sav_label | varchar(255) | NOT NULL | Label pour le SAV |
section | string (enum) | NULL | Section d'affichage front (pac_air_air, pac_air_eau). Permet de regrouper plusieurs catégories (ex. PAC Air/Air hors gainable + Gainable) dans une même section. |
hourly_price | float | NOT NULL | Prix horaire |
hour_require | float | NOT NULL | Heures requises |
supply_price | float | NOT NULL | Prix des fournitures |
individual_vat | float | NOT NULL | TVA pour particulier |
professional_vat | float | NOT NULL | TVA pour professionnel |
Relations :
ManyToOne → category (Equipment\Category)equipment_brand (Equipment\Brand)Marques d'équipements.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
name | varchar(255) | NOT NULL | Nom de la marque |
sav_label | varchar(255) | NOT NULL | Label pour le SAV |
maintenance_offer (MaintenanceOffer\MaintenanceOffer)Offres de maintenance disponibles.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
name | varchar(255) | NOT NULL | Nom de l'offre |
sav_label | varchar(255) | NULL | Label pour le SAV |
subscription_duration | int | NOT NULL | Durée d'engagement (en mois) |
subscription_visit | int | NOT NULL | Nombre de visites incluses |
description | text | NULL | Description de l'offre |
loyalty_discount | int | NULL | Réduction fidélité (en %) |
coefficient | float | NULL | Coefficient de calcul |
type | enum | NOT NULL | Type (RECURRENT, PUNCTUAL) |
best | boolean | NOT NULL, DEFAULT false | Offre recommandée |
position | int | NOT NULL | Position d'affichage |
Relations :
ManyToMany → features (MaintenanceOffer\Feature) via table maintenance_offer_featurefeature (MaintenanceOffer\Feature)Caractéristiques des offres.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
label | varchar(255) | NOT NULL | Libellé de la caractéristique |
troubleshooting (Troubleshooting\Troubleshooting)Demandes de dépannage.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | uuid | PK, NOT NULL | Identifiant unique |
address_id | int | FK, NOT NULL | Référence à address |
has_contract | boolean | NOT NULL | Possède un contrat |
details | text | NULL | Détails du problème |
availability | simple_array | NULL | Disponibilités (tableau) |
availability_details | text | NULL | Détails sur les disponibilités |
status | enum | NOT NULL | Statut (NEW, IN_PROGRESS, RESOLVED, CANCELLED) |
comments | text | NULL | Commentaires internes |
created_at | datetime_immutable | NOT NULL | Date de création |
updated_at | datetime_immutable | NOT NULL | Date de mise à jour |
Relations :
OneToOne → address (Address)ManyToMany → diagnosis (Troubleshooting\Diagnosis) via table troubleshooting_diagnosisdiagnosis (Troubleshooting\Diagnosis)Diagnostics possibles.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
name | varchar(255) | NOT NULL | Nom du diagnostic |
address (Address)Adresses (contact et facturation).
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
civility_id | int | FK, NULL | Référence à civility |
name | varchar(255) | NULL | Prénom |
lastname | varchar(255) | NULL | Nom |
is_pro | boolean | NOT NULL, DEFAULT false | Client professionnel |
company_name | varchar(255) | NULL | Nom de l'entreprise |
siret | varchar(255) | NULL | Numéro SIRET |
vat_number | varchar(255) | NULL | Numéro de TVA |
street | varchar(255) | NOT NULL | Rue |
additional_info | varchar(255) | NULL | Complément d'adresse |
postal_code | varchar(255) | NOT NULL | Code postal |
city | varchar(255) | NOT NULL | Ville |
email | varchar(255) | NULL | |
phone | varchar(255) | NULL | Téléphone |
Relations :
ManyToOne → civility (Civility)civility (Civility)Civilités.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
name | varchar(255) | NOT NULL | Nom de la civilité |
sav_label | varchar(255) | NULL | Label pour le SAV |
postal_code (PostalCode)Codes postaux avec tarification.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
code | varchar(5) | NOT NULL, UNIQUE | Code postal |
label | varchar(255) | NOT NULL | Libellé (ville) |
price | float | NULL | Prix de base |
diagnosis_price | float | NULL | Prix du diagnostic |
parameter (Parameter)Paramètres système.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
code | varchar(255) | NOT NULL, UNIQUE | Code du paramètre |
label | varchar(255) | NOT NULL | Libellé |
value | varchar(255) | NOT NULL | Valeur |
refusal_reason (RefusalReason)Raisons de refus.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
name | varchar(255) | NOT NULL | Nom de la raison |
coefficient_rate (CoefficientRate)Coefficients de tarification par catégorie.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
category_id | int | FK, NOT NULL | Référence à equipment_category |
min_equipment | int | NOT NULL | Nombre minimum d'équipements |
coefficient | float | NOT NULL | Coefficient appliqué |
Relations :
ManyToOne → category (Equipment\Category)admin (Admin)Comptes administrateurs.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
email | varchar(180) | NOT NULL, UNIQUE | Email de l'admin |
roles | json | NOT NULL | Rôles (tableau JSON) |
password | varchar(255) | NOT NULL | Mot de passe hashé |
Relations :
OneToMany → resetPasswordRequests (ResetPasswordRequest)OneToMany → refreshTokens (RefreshToken)reset_password_request (ResetPasswordRequest)Demandes de réinitialisation de mot de passe.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
user_id | int | FK, NOT NULL | Référence à admin |
selector | varchar(20) | NOT NULL | Sélecteur unique |
hashed_token | varchar(100) | NOT NULL | Token hashé |
requested_at | datetime_immutable | NOT NULL | Date de demande |
expires_at | datetime_immutable | NOT NULL | Date d'expiration |
Relations :
ManyToOne → user (Admin)refresh_tokens (RefreshToken)Tokens de rafraîchissement JWT.
| Colonne | Type | Contraintes | Description |
|---|---|---|---|
id | int | PK, AUTO_INCREMENT | Identifiant unique |
refresh_token | varchar(128) | NOT NULL, UNIQUE | Token de rafraîchissement |
username | varchar(255) | NOT NULL | Nom d'utilisateur |
valid | datetime_immutable | NOT NULL | Date de validité |
maintenance_offer_featureTable de jointure entre maintenance_offer et feature.
| Colonne | Type | Contraintes |
|---|---|---|
maintenance_offer_id | int | FK, PK |
feature_id | int | FK, PK |
troubleshooting_diagnosisTable de jointure entre troubleshooting et diagnosis.
| Colonne | Type | Contraintes |
|---|---|---|
troubleshooting_id | uuid | FK, PK |
diagnosis_id | int | FK, PK |
request_refusal_reasonTable de jointure entre request_refusal et refusal_reason.
| Colonne | Type | Contraintes |
|---|---|---|
request_refusal_id | uuid | FK, PK |
refusal_reason_id | int | FK, PK |
-- Index sur les clés étrangères
CREATE INDEX idx_request_offer_request ON request_offer(request_id);
CREATE INDEX idx_request_equipment_request ON request_equipment(request_id);
CREATE INDEX idx_request_contract_request ON request_contract(request_id);
CREATE INDEX idx_price_offer ON price(offer_id);
-- Index sur les recherches fréquentes
CREATE INDEX idx_request_status ON request(status);
CREATE INDEX idx_request_deleted_at ON request(deleted_at);
CREATE INDEX idx_request_archived_at ON request(archived_at);
CREATE INDEX idx_discount_code ON discount(code);
CREATE INDEX idx_postal_code_code ON postal_code(code);
CREATE INDEX idx_parameter_code ON parameter(code);
-- Index sur les dates
CREATE INDEX idx_request_created_at ON request(created_at);
CREATE INDEX idx_contract_expired_at ON request_contract(expired_at);
CREATE INDEX idx_troubleshooting_status ON troubleshooting(status);
La table request utilise le soft delete via le champ deleted_at. Les enregistrements supprimés ne sont pas physiquement supprimés mais marqués comme supprimés.
request expirent 4 mois après leur création (expired_at)contract expirent 90 jours après leur création (expired_at)RequestStatusEnum :
PENDING : En attenteWAITING_SIGNATURE : En attente de signatureWAITING_PAYMENT : En attente de paiementCOMPLETE : ComplétéeREJECTED : RejetéeContractStatusEnum :
PENDING : En attenteSIGNED : SignéEXPIRED : ExpiréCANCELLED : AnnuléTroubleshootingStatusEnum :
NEW : NouveauIN_PROGRESS : En coursRESOLVED : RésoluCANCELLED : AnnuléLes modifications de structure sont gérées via Doctrine Migrations :
# Créer une migration après modification des entités
php bin/console make:migration
# Exécuter les migrations
php bin/console doctrine:migrations:migrate
# Voir le statut des migrations
php bin/console doctrine:migrations:status
Les données de référence sont chargées via des fixtures :
# Charger les fixtures
php bin/console doctrine:fixtures:load
# Charger uniquement certaines fixtures
php bin/console doctrine:fixtures:load --group=equipment
deleted_at IS NULL dans les requêtesdatetime_immutable pour éviter les modifications accidentellescascade: ['persist', 'remove'] avec précautionorphanRemoval: true pour les relations OneToOne/OneToManyfetch: 'EAGER' ou des jointures explicites