Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .cursor/skills/align-docs-with-source/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name: align-docs-with-source
description: >-
Aligns documentation under docs/ with the current Rails codebase (models,
schema, services, specs). Use when syncing docs after refactors, before
merge to dev/main, or when the user asks to reconcile docs/ with source.
disable-model-invocation: true
---

# Align docs with source (Le Circographe)

## Goal

Keep Markdown under `docs/` consistent with **running code**, not aspirational text. Prefer **minimal edits** and **existing files** (no new docs unless the user asks).

## Sources of truth (read first)

1. `db/schema.rb` — columns, nullability, FKs.
2. Relevant `app/models/*.rb` (especially `User`, `Person`, `Payment`, `PaymentLine`).
3. Orchestration: `app/services/people/*.rb`, `app/services/account_claim_management/*.rb`.
4. Project lexicon: `docs/glossary.md`, `docs/migrations/vocabulary_migration.md`.
5. Cursor rules if identity/testing changed: `.cursor/rules/naming-rules.mdc`, `testing-auth-rules.mdc`.

## Checklist

- [ ] **Headers**: bump `Dernière vérification` on every touched public doc (`stable`).
- [ ] **User ↔ Person**: every `User` has a `Person` (`person_id` NOT NULL); a `Person` may have zero or one `User`. Linking via `People::AttachUserToPerson` / `People::AccountLinker`, not ad hoc controller assigns.
- [ ] **Payments / donations**: describe what `People::PaymentCreator` actually persists (`item_type` for donations); separate **legacy DB rows** from **current code** (see `docs/payments.md`, migrations `*donation*`).
- [ ] **Legacy vocabulary**: keep Ruby names (`BookOfEntry`, `SubscriptionPlan`, …) with **(cible : …)** per glossary — do not rename models in prose alone.
- [ ] **Cross-links**: `docs/README.md` index stays consistent with edited pages.

## Anti-patterns

- Copy-pasting old paragraphs after a refactor without grepping `app/`.
- Stating « optional User » where the DB requires `person_id`.
- Documenting `dependent: :nullify` on `Person` ↔ `User` if code uses `restrict_with_error`.

## Verification

After edits: quick grep in `docs/` for stale phrases (`User sans Person`, `réécrit.*Payment`, `dependent: :nullify` on User) and fix or contextualize.
23 changes: 23 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ AllCops:
- 'vendor/**/*'
- 'tmp/**/*'
- '.bundle/**/*'
- 'test/**/*'

# Ezam override kept on purpose (not enforced by Omakase)
Layout/EndOfLine:
Expand All @@ -27,6 +28,8 @@ Layout/EndOfLine:
# Phase 2 Batch 1 — LOW: Gemfile gem order within groups (non-domain)
Bundler/OrderedGems:
Enabled: true
Bundler/DuplicatedGem:
Enabled: true

# Lot B2 — Omakase disables the Rails department by default except a few test-only
# cops (e.g. AssertNot). We selectively enable high-value Rails cops here; add
Expand All @@ -43,7 +46,27 @@ Rails/PluralizationGrammar:
Enabled: true
Rails/UniqBeforePluck:
Enabled: true
Rails/Pluck:
Enabled: true
Rails/WhereEquals:
Enabled: true
Rails/Present:
Enabled: true
Rails/SkipsModelValidations:
Enabled: true
Exclude:
- 'docs/rake_archive/**/*.rake'
Rails/HelperInstanceVariable:
Enabled: true
Rails/I18nLocaleTexts:
Enabled: true
Performance/RedundantEqualityComparisonBlock:
Enabled: true
Performance/CollectionLiteralInLoop:
Enabled: true
Performance/MapMethodChain:
Enabled: true
Performance/Sum:
Enabled: true
Performance/Detect:
Enabled: true
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Le projet utilise un vocabulaire DDD-light strict. **Avant toute contribution, l
- [docs/payments.md](docs/payments.md) — paiements, lignes, dons.
- [docs/migrations/vocabulary_migration.md](docs/migrations/vocabulary_migration.md) — plan de migration en cours.

> Résumé express : `Person` (CRM) ⟶ `User` (compte web optionnel) ⟶ `Membership` (adhésion annuelle) ⟶ `Contribution` (cotisation cirque) selon une `ContributionFormula`. Les paiements (`Payment`) regroupent une ou plusieurs `PaymentLine` (adhésion, cotisation, don).
> Résumé express : `Person` (CRM) ⟶ `User` (compte web, toujours rattaché à une `Person`) ⟶ `Membership` (adhésion annuelle) ⟶ `Contribution` (cotisation cirque) selon une `ContributionFormula`. Les paiements (`Payment`) regroupent une ou plusieurs `PaymentLine` (adhésion, cotisation, don).

---

Expand All @@ -35,12 +35,17 @@ Application disponible sur `http://localhost:3000`. Letter Opener Web (emails de

---

## Tests
## Tests (RSpec only)

Le projet utilise **RSpec uniquement**. Le legacy Minitest (`test/`) a ete retire.
La stack d'authentification reste **native Rails 8** (pas Devise).

```bash
bin/test # suite complète + couverture
bin/test_fast # models + services (rapide)
bin/test --no-coverage # sans SimpleCov
bundle exec rspec # commande RSpec canonique
bundle exec rubocop --force-exclusion
```

---
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/account_claims_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def create
redirect_to root_path, alert: result.message
end
rescue StandardError => e
redirect_to root_path, alert: "Erreur: #{e.message}"
redirect_to root_path, alert: t(".rescue_alert", message: e.message)
end

def confirm
Expand All @@ -37,6 +37,6 @@ def confirm
redirect_to root_path, alert: result.message
end
rescue StandardError => e
redirect_to root_path, alert: "Erreur lors de la réclamation: #{e.message}"
redirect_to root_path, alert: t(".rescue_alert", message: e.message)
end
end
12 changes: 6 additions & 6 deletions app/controllers/admin/attendance_lists_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ class AttendanceListsController < BaseController

def index
@attendance_list = AttendanceList.order(created_at: :desc)
add_breadcrumb "Listes de présence", nil
add_breadcrumb I18n.t("breadcrumbs.admin.attendance_lists.lists"), nil
end

def show
add_breadcrumb "Listes de présence", admin_attendance_lists_path
add_breadcrumb I18n.t("breadcrumbs.admin.attendance_lists.lists"), admin_attendance_lists_path
add_breadcrumb @attendance_list.name, nil
end

def new
add_breadcrumb "Listes de présence", admin_attendance_lists_path
add_breadcrumb "Nouvelle liste", nil
add_breadcrumb I18n.t("breadcrumbs.admin.attendance_lists.lists"), admin_attendance_lists_path
add_breadcrumb I18n.t("breadcrumbs.admin.attendance_lists.new_list"), nil
end

def edit
add_breadcrumb "Listes de présence", admin_attendance_lists_path
add_breadcrumb I18n.t("breadcrumbs.admin.attendance_lists.lists"), admin_attendance_lists_path
add_breadcrumb @attendance_list.name, admin_attendance_list_path(@attendance_list)
add_breadcrumb "Modifier", nil
add_breadcrumb I18n.t("breadcrumbs.admin.common.edit"), nil
end

def create
Expand Down
10 changes: 5 additions & 5 deletions app/controllers/admin/attendances_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@ def index
# Pagination
@attendances = @attendances.order(date: :desc).page(params[:page]).per(20)

add_breadcrumb "Gestion des présences", nil
add_breadcrumb I18n.t("breadcrumbs.admin.attendances.management"), nil
end

def show
add_breadcrumb "Gestion des présences", admin_attendances_path
add_breadcrumb "Présence ##{@attendance.id}", nil
add_breadcrumb I18n.t("breadcrumbs.admin.attendances.management"), admin_attendances_path
add_breadcrumb I18n.t("breadcrumbs.admin.attendances.attendance_number", id: @attendance.id), nil
end

def new
@attendance = Attendance.new
@people = Person.order(:first_name, :last_name)
@events = Event.upcoming.order(:date)

add_breadcrumb "Gestion des présences", admin_attendances_path
add_breadcrumb "Nouvelle présence", nil
add_breadcrumb I18n.t("breadcrumbs.admin.attendances.management"), admin_attendances_path
add_breadcrumb I18n.t("breadcrumbs.admin.attendances.new_attendance"), nil
end

def create
Expand Down
10 changes: 5 additions & 5 deletions app/controllers/admin/events_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@ class EventsController < BaseController

def index
@events = Event.all
add_breadcrumb "Événements", nil
add_breadcrumb I18n.t("breadcrumbs.admin.events.events"), nil
end

def new
@event = Event.new
add_breadcrumb "Événements", admin_events_path
add_breadcrumb "Nouvel événement", nil
add_breadcrumb I18n.t("breadcrumbs.admin.events.events"), admin_events_path
add_breadcrumb I18n.t("breadcrumbs.admin.events.new_event"), nil
end

def edit
@event = Event.find params[:id]
add_breadcrumb "Événements", admin_events_path
add_breadcrumb I18n.t("breadcrumbs.admin.events.events"), admin_events_path
add_breadcrumb @event.title, event_path(@event)
add_breadcrumb "Modifier", nil
add_breadcrumb I18n.t("breadcrumbs.admin.common.edit"), nil
end

def create
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/admin/health_reports_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ def index
@duplicate_people_by_phone_groups = report.duplicate_people_by_phone.group_by { |person| person.phone.to_s }
@list_limit = Admin::HealthReport::MAX_LIST

add_breadcrumb "Rapport d'intégrité", nil
add_breadcrumb I18n.t("breadcrumbs.admin.health_reports.integrity_report"), nil
end

private

def set_breadcrumbs
add_breadcrumb "Administration", admin_dashboard_index_path
add_breadcrumb I18n.t("breadcrumbs.admin.common.administration"), admin_dashboard_index_path
end
end
end
12 changes: 6 additions & 6 deletions app/controllers/admin/membership_types_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ class MembershipTypesController < BaseController

def index
@membership_types = MembershipType.order(:category, :price_cents)
add_breadcrumb "Types d'Adhésion", nil
add_breadcrumb I18n.t("breadcrumbs.admin.membership_types.types"), nil
end

def show
@memberships = @membership_type.memberships.includes(:person).order(created_at: :desc).limit(10)
add_breadcrumb "Type: #{@membership_type.name}", nil
add_breadcrumb I18n.t("breadcrumbs.admin.membership_types.type_named", name: @membership_type.name), nil
end

def new
Expand All @@ -21,11 +21,11 @@ def new
version: 1,
created_by_user: Current.user
)
add_breadcrumb "Nouveau type d'adhésion", nil
add_breadcrumb I18n.t("breadcrumbs.admin.membership_types.new_type"), nil
end

def edit
add_breadcrumb "Modifier: #{@membership_type.name}", nil
add_breadcrumb I18n.t("breadcrumbs.admin.membership_types.edit_named", name: @membership_type.name), nil
end

def create
Expand Down Expand Up @@ -69,8 +69,8 @@ def set_membership_type
end

def set_breadcrumbs
add_breadcrumb "Administration", admin_dashboard_index_path
add_breadcrumb "Types d'Adhésion", admin_membership_types_path
add_breadcrumb I18n.t("breadcrumbs.admin.common.administration"), admin_dashboard_index_path
add_breadcrumb I18n.t("breadcrumbs.admin.membership_types.types"), admin_membership_types_path
end

def membership_type_params
Expand Down
43 changes: 27 additions & 16 deletions app/controllers/admin/memberships_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ class MembershipsController < BaseController

def index
@people = Person.includes(:memberships, :user).all
add_breadcrumb "Gestion des Adhésions", nil
add_breadcrumb I18n.t("breadcrumbs.admin.memberships.management"), nil
end

def show
@membership = @person.current_membership
@membership_types = MembershipType.all
@contribution_formulas = ContributionFormula.all

add_breadcrumb "Liste d'adhérents", admin_users_path
add_breadcrumb I18n.t("breadcrumbs.admin.users.members_list"), admin_users_path
add_breadcrumb @person.full_name, admin_user_path("person_#{@person.id}")
add_breadcrumb "Adhésion", nil
add_breadcrumb I18n.t("breadcrumbs.admin.memberships.membership"), nil
end

def new
Expand All @@ -30,19 +30,19 @@ def new
@membership_types = MembershipType.circus_types.current_versions.order(:price_cents)
@is_upgrade = true
@current_membership = @person.current_membership
add_breadcrumb "Upgrade vers Cirque", nil
add_breadcrumb I18n.t("breadcrumbs.admin.memberships.upgrade_to_circus"), nil
else
# Pour une nouvelle adhésion, on propose tous les types
@membership_types = MembershipType.current_versions.order(:price_cents)
@is_upgrade = false
add_breadcrumb "Nouvelle adhésion", nil
add_breadcrumb I18n.t("breadcrumbs.admin.memberships.new_membership"), nil
end
end

def edit
@membership = @person.current_membership
@membership_types = MembershipType.all
add_breadcrumb "Modifier adhésion", nil
add_breadcrumb I18n.t("breadcrumbs.admin.memberships.edit_membership"), nil
end

def create
Expand Down Expand Up @@ -102,7 +102,7 @@ def set_person_for_create
end

def set_breadcrumbs
add_breadcrumb "Administration", admin_dashboard_index_path
add_breadcrumb I18n.t("breadcrumbs.admin.common.administration"), admin_dashboard_index_path
end

def membership_params
Expand Down Expand Up @@ -146,7 +146,7 @@ def handle_upgrade_flow(person, membership_type)
redirect_to admin_user_path("person_#{person.id}"), notice: build_upgrade_notice(membership_type, result, payment_method_from(params_hash))
else
redirect_to new_admin_membership_path(person_id: person.id, upgrade: params_hash[:upgrade]),
alert: "Erreur lors de l'upgrade: #{result.message}"
alert: t("admin.memberships.create.upgrade_failed_alert", message: result.message)
end
end

Expand All @@ -172,19 +172,30 @@ def handle_creation_flow(person, membership_type)
end
else
redirect_to new_admin_membership_path(person_id: person.id),
alert: "Erreur lors de la création de l'adhésion: #{result.message}"
alert: t("admin.memberships.create.membership_creation_failed_alert", message: result.message)
end
end

def build_upgrade_notice(membership_type, result, payment_method)
message = if payment_method == "offered"
"Adhésion upgradée avec succès ! #{membership_type.name} - Offert"
else
amount = result.payment&.total_cents || membership_type.price_cents
"Adhésion upgradée avec succès ! #{membership_type.name} - Montant: #{(amount / 100.0).round(2)}€"
end
message =
if payment_method == "offered"
I18n.t("admin.memberships.create.upgrade_notice_offered", name: membership_type.name)
else
amount = result.payment&.total_cents || membership_type.price_cents
I18n.t(
"admin.memberships.create.upgrade_notice_paid",
name: membership_type.name,
amount: (amount / 100.0).round(2)
)
end

message += " | Numéro d'adhérent changé: #{result.old_member_number} → #{result.new_member_number}" if result.member_number_changed
if result.member_number_changed
message += I18n.t(
"admin.memberships.create.upgrade_notice_member_number_suffix",
old_number: result.old_member_number,
new_number: result.new_member_number
)
end

message
end
Expand Down
6 changes: 3 additions & 3 deletions app/controllers/admin/opening_hours_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ class OpeningHoursController < BaseController
include OpeningHoursHelper

def show
add_breadcrumb "Horaires d'ouverture", nil
add_breadcrumb I18n.t("breadcrumbs.admin.opening_hours.title"), nil
end

def edit
add_breadcrumb "Horaires d'ouverture", admin_opening_hours_path
add_breadcrumb "Modifier", nil
add_breadcrumb I18n.t("breadcrumbs.admin.opening_hours.title"), admin_opening_hours_path
add_breadcrumb I18n.t("breadcrumbs.admin.common.edit"), nil
end

def update
Expand Down
Loading
Loading