Statut : stable Public cible : contributeur Dernière vérification : 2026-05-01 Sources de vérité :
db/schema.rb,app/models/person.rb,app/models/membership.rb,app/models/payment.rb,app/models/payment_line.rb.
Vocabulaire utilisé : voir glossary.md. Cette doc emploie le vocabulaire canonique. Pattern : Person-Based / DDD-light.
erDiagram
Person ||--o| User : "≤1 compte web par Person"
Person ||--o{ Membership : "souscrit"
Person ||--o{ Contribution : "achète"
Person ||--o{ Attendance : "présence"
Person ||--o{ Payment : "règle"
Person ||--o{ EventAttendee : "inscrit"
Person ||--o| NewsletterSubscriber : "lié par email"
MembershipType ||--o{ Membership : "type"
ContributionFormula ||--o{ Contribution : "formule"
Event ||--o{ EventAttendee : "inscriptions"
AttendanceList ||--o{ Attendance : "regroupe"
Payment ||--o{ PaymentLine : "contient"
PaymentLine }o..|| Membership : "polymorphic item"
PaymentLine }o..|| ContributionFormula : "polymorphic item"
PaymentLine }o..|| MembershipType : "polymorphic item"
PaymentLine }o..|| Donation : "polymorphic item (cible)"
Person ||--o{ MemberNumberHistory : "audit numéro"
Payment ||--o{ PaymentAuditLog : "audit transactionnel"
Légende :
ContributionetContributionFormulasont les noms canoniques.Donation: pas encore un modèle ActiveRecord dédié ; lignesPaymentLineen"Donation"à la création ; lignes historiquesitem_type: "Payment"encore possibles (voir payments.md).
- Compte web :
has_one :user, dependent: :restrict_with_error— au plus unUser; pas de suppression « dure » de la fiche tant qu'un compte web existe (flux RGPD / archive à utiliser). - Identité :
full_name,phone,email,address,birth_date. - Tarif réduit :
reduced_rate_eligible,reduced_rate_reason,reduced_rate_proof. - Soft delete :
deleted_at(concernSoftDeletable). - Méthodes métier :
People::MembershipCreator— adhésion + paiement + numéro d'adhérent.People::MembershipUpgrader— plein tarif du nouveau type.Person#renew_membership!(...)— nouvelle adhésion + nouveau numéro annuel.People::ContributionCreator— création cotisation + paiement.People::ContributionUpgrader— upgrade cotisation + paiement.Person#archive!/Person#restore!.
- Garde-fou :
has_financial_data?empêche la suppression dure si l'historique financier est non vide.
- Invariant :
belongs_to :personobligatoire en base (users.person_idNOT NULL). À la création sanspersonexplicite, le modèle attache unePersonminimale (prénom/nom stub + email aligné sur le compte). - Authentification : email, password, sessions, password_reset_token.
- Rôle :
system_roleenum:super_admin | :admin | :volunteer | :web_visitor. - Délégation :
delegate :full_name, :phone, ... to: :person. - Liaison / fusion : rattachement nominal
People::AttachUserToPerson; orchestration admin/scriptsPeople::AccountLinker; fusion de fichesPeople::AccountMerger. - Soft delete :
User#archive!(admin uniquement).
- Lien :
belongs_to :person,belongs_to :membership_type. - Dates :
started_at,ended_at(1 an par défaut). - Statuts :
:pending → :active → :inactive → :expired. - Validations :
ended_at > started_at, pas d'overlap actif (saufskip_overlap_validation). - Méthodes :
#can_upgrade_to?(type),#upgrade_to!(type, started_at),#basic?,#circus?.
- Champs :
name,category(:basic | :circus | :event),price_cents,version,effective_from,effective_until,created_by_user_id,change_reason. - Concern :
Versionable.
- Lien :
belongs_to :person,belongs_to :contribution_formula. - Champs :
sessions_remaining(Pack 10 uniquement),purchased_at,expires_at,status. - Statuts :
:inactive | :active | :expired | :consumed | :suspended. - Méthodes :
#can_use?— vérifie adhésion Cirque active + sessions restantes.#use_session!— décrémentesessions_remaining(Pack 10).#refund_session!— recrédite (annulation présence).#suspend!(reason:)/#reactivate!.
- Note :
dayest une cotisation à usage unique valable jusqu'à la fin du jour d'achat ;trimesteretannualrestent des cotisations à durée avec expiration.
- Champs :
name,durationenum (:day | :trimester | :annual | :pack10),price_cents,sessions_count,validity_days,version,effective_from,effective_until. - Méthode :
.available_for(person)— exige une adhésion Cirque active.
- Lien :
belongs_to :person,belongs_to :recorded_by, class_name: "User". - Champs :
total_cents,status(:pending | :success | :cancel),payment_method(:cash | :card | :cheque | :transfer | :offered),uuid,notes. offer_reasonest persisté danspaymentset requis pour un paiement:offered.- RGPD :
Payment#anonymize!gardepayments.person_id, stocke un hash de traçabilité dansoriginal_person_identifieret marqueanonymized_at. - Audit :
PaymentAuditLog.
- Champs :
payment_id,item_type,item_id,amount_cents,description. - Items canoniques :
Membership,MembershipType,ContributionFormula,Contribution,Donation. - Invariant :
payment.payment_lines.sum(:amount_cents) == payment.total_cents.
- Pas de table dédiée. Représenté par une
PaymentLineavecitem_type: "Donation"à la création (People::PaymentRecorder). Vérifier les données historiquesitem_type: "Payment"avant suppression de compatibilité — voir payments.md.
- Lien :
belongs_to :person,belongs_to :attendance_list(optionnel),belongs_to :event(optionnel),belongs_to :contribution. - Règles d'unicité :
person_id + date(entraînement libre) ouperson_id + event_id(événement). - Effet de bord : décrémente la cotisation utilisée si applicable.
- Statuts :
:open | :close | :archived. - Génération quotidienne :
AttendanceListManagement::DailyListGenerator(skip lundi).
- Event :
name,event_date,category(default::circus). - EventAttendee : jointure
Person × Eventavec unicitéperson_id + event_id.
- Trace tout changement de numéro d'adhérent (
from_number,to_number,reason,changed_by).
- Trace toute opération sur
Payment(création, annulation, restauration, anonymisation).
- Workflow de réclamation de compte :
:pending → :confirmed | :rejected | :expiredavec token sécurisé.
- Table indépendante de
Person. Lien automatique si l'email correspond.
stateDiagram-v2
[*] --> pending : create_membership
pending --> active : paiement réussi
active --> inactive : upgrade vers nouveau type
inactive --> [*] : remplacée
active --> expired : ended_at < today
expired --> [*] : renew_membership crée nouvelle
stateDiagram-v2
[*] --> active : create_contribution
active --> consumed : sessions_remaining == 0 (Pack 10)
active --> expired : expires_at < today (non Pack 10)
active --> suspended : adhésion Cirque expirée
suspended --> active : nouvelle adhésion Cirque active
active --> suspended : upgrade Pack 10 → Trimestre/Annuel
stateDiagram-v2
[*] --> pending : create_payment
pending --> success : success!
pending --> cancel : cancel!
success --> [*]
cancel --> [*]
sequenceDiagram
participant UI as Admin UI
participant Reg as People::Register
participant PC as People::PersonCreator
participant UAC as People::UserAccountCreator
participant MC as People::MembershipCreator
participant PayR as People::PaymentRecorder
UI->>Reg: call(person_attrs, user_attrs?, membership_type_id?)
Reg->>PC: create Person
PC-->>Reg: Person
alt user_attrs présents
Reg->>UAC: create User pour Person
UAC-->>Reg: User
end
alt membership_type_id présent
Reg->>MC: create Membership + Payment
MC->>PayR: create Payment + PaymentLine
PayR-->>MC: Payment
MC-->>Reg: Membership
end
Reg-->>UI: success(person, user?, membership?)
Inscription web : les flux qui ne passent pas par
People::Registercréent tout de même un coupleUser+Personvia le callbackUser(personne minimale), puis enrichissement CRM au fil du temps.
- glossary.md — vocabulaire canonique.
- payments.md — détail Payment / PaymentLine / Donation.
- migrations/vocabulary_migration.md — mapping ancien → nouveau.
- domain/business_logic.md — règles métier complètes.
- architecture/services.md — services
People::*et orchestrateurs (Register,AttachUserToPerson,AccountLinker, paiements).