Aller au contenu

Référence: Individual Capabilities And Quotas

Objectif

Poser un document de référence stable pour le modèle cible V1 des capabilities et quotas du compte individual.

Ce document couvre:

  • le socle runtime déjà en place autour du plan effectif
  • l'existant réel sur la solidarité
  • les manques actuels sur event, event registration et event proposal
  • le catalogue canonique V1 retenu et implémenté côté backend
  • les règles d'enforcement métier attendues

Statut: référence V1 implémentée côté backend, valeurs plans encore à peupler via admin/front Date de mise à jour: 2026-04-12

Périmètre

  • Compte individual uniquement
  • Pas de traitement du modèle pro sauf pour éviter les collisions de vocabulaire
  • Point de vue "capabilities et quotas liés à la souscription effective"
  • Cycle temporel de référence: mois de souscription ancré sur person_subscriptions.cycle_anchor_at

Décisions Retenues

  • Le runtime individual repose sur la souscription effective explicite, pas sur un fallback implicite vers le plan par défaut.
  • V1 ne retient pas de quotas future_*.
  • V1 retient des quotas mensuels de consommation d'actions réalisées.
  • V1 retient aussi des quotas de gabarit pour limiter la taille maximale d'un event ou d'une event_proposal.
  • event_proposal doit avoir son propre quota de création, car une proposal est déjà une action sortante vers d'autres utilisateurs.
  • Pour event_proposal, limiter seulement le nombre de créations ne suffit pas: il faut aussi borner le nombre de places, et idéalement le nombre d'invitées.

État Actuel Du Code

Plan effectif

Source de vérité actuelle:

  • person_subscriptions porte la souscription explicite de la personne
  • services/subscription/active_plan.go charge la souscription et le plan effectifs
  • le signup matérialise une vraie souscription sur le plan par défaut

Points importants:

  • le plan par défaut sert à attribuer une souscription gratuite initiale
  • le runtime ne doit plus "inventer" un plan gratuit si la souscription explicite manque
  • le mois de souscription se calcule à partir de cycle_anchor_at

Références code:

  • models/person_subscription.go
  • services/subscription/active_plan.go
  • services/subscription/person_subscription_write.go
  • services/signup/service.go

Capabilities existantes

Le plan porte aujourd'hui uniquement deux familles de droits:

  • des droits solidarité "person" par module et par intention
  • des capabilities organisateur pour la configuration de la solidarité d'un event

Catalogue actuel côté plan:

  • hosting.offer
  • hosting.request
  • carpool.offer
  • carpool.request
  • equipment.offer
  • equipment.request
  • enable_hosting
  • enable_carpool
  • enable_equipment
  • allow_external_helpers

Références code:

  • models/subscription_plan.go
  • models/solidarity_rights.go
  • models/solidarity_event_capabilities.go
  • services/subscription/plan_parsing.go
  • services/subscription/plan_format.go

Quotas existants

Les quotas plans existants sont aujourd'hui strictement limités au domaine solidarité.

Catalogue actuel:

  • module=hosting|carpool|equipment
  • metric=open_requests
  • metric=consumed_completed
  • period=none
  • period=subscription_month

Usage runtime:

  • open_requests borne le stock de demandes OPEN
  • consumed_completed borne le nombre d'aides complétées pendant le mois de souscription

Des bonus de quotas peuvent être ajoutés via benefit_tiers.

Références code:

  • models/plan_quota.go
  • models/benefit_tier.go
  • services/solidarity/resolver.go

Overrides existants

Overrides réellement branchés au runtime:

  • overrides de droits solidarité au niveau personne
  • overrides de droits solidarité au niveau event

Override présent en modèle mais pas branché au runtime actuel:

  • person_solidarity_event_capabilities_overrides

Références code:

  • models/person_solidarity_rights_override.go
  • models/event_solidarity_rights_override.go
  • models/person_solidarity_event_capabilities_override.go
  • services/solidarity/resolver.go

Exposition au front

Le front peut aujourd'hui lire:

  • le plan effectif courant via GET /me/subscription
  • la configuration admin d'un plan via GET /subscription-plans et GET /subscription-plans/{idPlan}
  • les organizer capabilities solidarité via GET /solidarity/organizer-capabilities
  • les self capabilities solidarité request/offer sur un event

Expose réellement aujourd'hui:

  • /me/subscription expose le plan effectif, les rights, les organizerCapabilities, les capabilities canoniques, les quotas et le quotaUsage des quotas mensuels consommables
  • les endpoints admin subscription-plans exposent rights, organizerCapabilities, capabilities côté individual, quotas legacy solidarité et individualQuotas
  • le snapshot riche solidarity.GetCapabilities() existe mais n'est pas exposé

Doc front dédiée:

Références code:

  • controllers/subscription_plans.go
  • models/subscription_plan_dto.go
  • controllers/solidarity_organizer_capabilities.go
  • controllers/event.go
  • services/solidarity/self_capabilities_service.go
  • services/solidarity/resolver_types.go

Enforcement runtime réel

Ce qui est déjà contrôlé au moment des actions:

  • création de solidarity_request
  • création de solidarity_proposal
  • completion des assignments solidarité
  • activation/configuration de la solidarité sur un event
  • création d'event
  • inscription à un event
  • création d'event proposal
  • patch event proposal sur les caps maxSeats / invitées
  • auto-conversion event proposal -> event avec revérification du create event

Références code:

  • services/solidarity/request_service.go
  • services/solidarity/proposal_service.go
  • services/solidarity/assignment_service.go
  • services/solidarity/event_config_service.go
  • services/eventsvc/service.go
  • services/eventsvc/participation.go
  • services/eventproposal/write.go
  • services/eventproposal/event_creator.go

Manques À Combler Côté Individual

Valeurs plans encore absentes

  • peupler les vraies valeurs individual plan par plan
  • vérifier les choix produit finaux à travers l'admin/front avant seed ou migration définitive

Lectures runtime encore absentes

  • exposer used / remaining si le front veut afficher la consommation réelle des quotas mensuels
  • exposer un catalogue dynamique si l'on veut éviter le hardcode front des clés autorisées

Points de contrat à garder explicites

  • côté admin, rights et organizerCapabilities restent la source de vérité pour la solidarité
  • côté admin, capabilities ne porte que le sous-ensemble individual
  • côté self, GET /me/subscription expose un catalogue capabilities canonique de lecture et des quotas de limite seulement

Références code:

  • controllers/subscription_plans.go
  • services/eventproposal/write.go
  • services/eventproposal/event_creator.go

Modèle Cible V1

Principe général

Le modèle cible V1 doit distinguer 3 choses:

  • une capability booléenne: l'action existe pour ce plan
  • un quota de consommation mensuel: combien de fois l'action peut être réalisée sur le mois de souscription
  • un quota de gabarit: taille maximale autorisée pour l'objet créé ou modifié

V1 ne retient pas de quotas de stock futur.

Catalogue canonique V1 des capabilities stockées

Capabilities de participation/event:

  • events.individual.create
  • events.register
  • event_proposals.create

Capabilities de configuration event solidarité:

  • event_solidarity.hosting.enable
  • event_solidarity.carpool.enable
  • event_solidarity.equipment.enable
  • event_solidarity.external_helpers.allow

Capabilities solidarité personne:

  • solidarity.hosting.request
  • solidarity.hosting.offer
  • solidarity.carpool.request
  • solidarity.carpool.offer
  • solidarity.equipment.request
  • solidarity.equipment.offer

Capabilities dérivées mais non stockées

Ces capabilities peuvent être dérivées à la lecture et ne doivent pas devenir la source de vérité:

  • event_solidarity.configure
  • solidarity.request
  • solidarity.offer

Mapping de transition depuis l'existant

  • hosting.request -> solidarity.hosting.request
  • hosting.offer -> solidarity.hosting.offer
  • carpool.request -> solidarity.carpool.request
  • carpool.offer -> solidarity.carpool.offer
  • equipment.request -> solidarity.equipment.request
  • equipment.offer -> solidarity.equipment.offer
  • enable_hosting -> event_solidarity.hosting.enable
  • enable_carpool -> event_solidarity.carpool.enable
  • enable_equipment -> event_solidarity.equipment.enable
  • allow_external_helpers -> event_solidarity.external_helpers.allow

Catalogue canonique V1 des quotas

Quotas mensuels de consommation

Events

  • events.individual.create_per_subscription_month
  • 1 unité consommée quand un event est créé avec succès
  • pas de remboursement si l'event est ensuite supprimé ou annulé

  • events.register_per_subscription_month

  • 1 unité consommée à la première inscription réussie sur un event donné pendant la période courante
  • un unregister ne rembourse pas
  • un register ultérieur sur le même event dans la même période ne doit pas reconsommer

Event proposals

  • event_proposals.create_per_subscription_month
  • 1 unité consommée quand une proposal est créée avec succès
  • pas de remboursement si la proposal expire, est annulée ou ne se convertit jamais

Solidarité

  • solidarity.hosting.requests.completed_per_subscription_month
  • solidarity.carpool.requests.completed_per_subscription_month
  • solidarity.equipment.requests.completed_per_subscription_month

Ces quotas correspondent à l'actuel consumed_completed.

Quotas de gabarit

Events

  • events.individual.max_seats_per_event
  • borne la valeur maximale de maxSeats
  • doit être enforce à la création et au patch

Event proposals

  • event_proposals.max_seats_per_proposal
  • borne la valeur maximale de maxSeats
  • doit être enforce à la création et au patch

  • event_proposals.max_invitees_per_proposal

  • borne le nombre total d'invitées uniques d'une proposal
  • doit être enforce à la création et sur le patch add-only des invitées
  • ce quota est retenu car max_seats_per_proposal seul ne suffit pas à borner la surface de spam

Solidarité

  • solidarity.hosting.requests.open
  • solidarity.carpool.requests.open
  • solidarity.equipment.requests.open

Ces quotas correspondent à l'actuel open_requests.

Règles D'Enforcement V1

Event create

Service cible:

  • services/eventsvc.Service.CreateEventV2

Enforcement attendu:

  • capability events.individual.create
  • quota events.individual.create_per_subscription_month
  • quota de gabarit events.individual.max_seats_per_event

Event patch

Service cible:

  • services/eventsvc.Service.UpdateEvent

Enforcement attendu:

  • si maxSeats augmente ou est modifié:
  • revalider events.individual.max_seats_per_event
  • le patch ne doit pas reconsommer le quota mensuel de création

Event register

Service cible:

  • services/eventsvc.Service.RegisterForEvent

Enforcement attendu:

  • capability events.register
  • quota events.register_per_subscription_month
  • consommation idempotente par (person_id, event_id, periode_de_souscription)

Event proposal create

Service cible:

  • services/eventproposal.Service.Create

Enforcement attendu:

  • capability event_proposals.create
  • quota event_proposals.create_per_subscription_month
  • quota de gabarit event_proposals.max_seats_per_proposal
  • quota de gabarit event_proposals.max_invitees_per_proposal

Event proposal patch

Service cible:

  • services/eventproposal.Service.Patch

Enforcement attendu:

  • si maxSeats change:
  • revalider event_proposals.max_seats_per_proposal
  • si des invitées sont ajoutées:
  • revalider event_proposals.max_invitees_per_proposal
  • le patch ne doit pas reconsommer le quota mensuel de création

Event proposal conversion vers event

Services cibles:

  • services/eventproposal.Service.maybeAutoConvertProposalToEvent
  • services/eventproposal.EventCreatorImpl.CreateEventFromProposalSlot

Enforcement attendu:

  • la conversion doit revalider la capability events.individual.create
  • la conversion doit consommer events.individual.create_per_subscription_month si aucun event n'a encore été créé pour cette proposal
  • la conversion ne doit pas bypass events.individual.max_seats_per_event
  • une proposal conforme côté proposal ne doit pas pouvoir produire un event non conforme côté event

Recommandation Nette Bool vs Quota

Doit être une capability booléenne:

  • events.individual.create
  • events.register
  • event_proposals.create
  • toutes les capabilities event_solidarity.*
  • toutes les capabilities solidarity.*.request|offer

Doit être un quota mensuel de consommation:

  • events.individual.create_per_subscription_month
  • events.register_per_subscription_month
  • event_proposals.create_per_subscription_month
  • solidarity.*.requests.completed_per_subscription_month

Doit être un quota de gabarit:

  • events.individual.max_seats_per_event
  • event_proposals.max_seats_per_proposal
  • event_proposals.max_invitees_per_proposal
  • solidarity.*.requests.open

Hors Scope V1

  • quotas future_*
  • remboursement de quota après delete/cancel/unregister
  • quotas helper côté solidarité
  • quota mensuel sur les updates d'event ou de proposal
  • monétisation ou quota spécifique des réponses aux event_proposals

Notes De Mise En Œuvre

  • Le modèle actuel SubscriptionPlan et PlanQuota est encore solidarité-centrique.
  • L'implémentation V1 a introduit un catalogue individual dédié sans casser le runtime solidarité déjà en place.
  • Tant que la migration n'est pas faite, il faut garder une couche de mapping explicite entre l'ancien catalogue solidarité et le nouveau catalogue canonique individual.
  • Les valeurs effectives des plans individual ne sont pas encore peuplées en base à la date de cette mise à jour.