Membership & Licensing
122 features · 10 subsystems
Complete lifecycle management of player licenses, club memberships, federation affiliations, and inter-federation transfers. This is the backbone of the petanque administrative system.
License Management
F03.01- F03.01.01 Shipped
Configurable license types per tenant — each federation defines its own types (e.g., Sweden: 1 type; France: compétition, loisir, jeune, découverte). Each type has: competition eligibility, insurance inclusion, age limits, duration, renewability
✅ PL-F0301a - F03.01.02 Shipped
License application workflow (club applies on behalf of player → federation reviews → approves → issues)
✅ PL-F0301a - F03.01.03 Shipped
License numbering system (federation prefix + sequential, configurable format per tenant)
✅ PL-F0301a - F03.01.04 Shipped
Annual license renewal (individual and bulk), season configurable per tenant (calendar year default)
✅ PL-F0301a - F03.01.05 Shipped
Composite license fee calculation — fee = sum of configurable components (e.g., part fédérale + part ligue/region + part comité/district + assurance). Each component set at its OrgNode level. Supports regional price variation (France, Spain)
✅ PL-F0301a - F03.01.06 Shipped
License card generation (digital + physical), with FIPJP symbol where required — PL-F0301b
✅ PL-F0301b - F03.01.07 Shipped
License verification — QR code scan at events, public API for cross-tenant verification — PL-F0301b
✅ PL-F0301b - F03.01.08 Shipped
License history tracking (all clubs, all federations, all years) — PL-F0301b
✅ PL-F0301b - F03.01.09 Shipped
License suspension and revocation — only federation can decide (not club/district). Administrative fee configurable. — PL-F0301b
✅ PL-F0301b - F03.01.10 Shipped
Time-limited license type — e.g., France's "licence découverte" (max 3 months, one-time only per player) — PL-F0301b
✅ PL-F0301b - F03.01.11 Shipped
Grace period management for late renewals — PL-F0301c
✅ PL-F0301c - F03.01.12 Shipped
Bulk license import from legacy systems — PL-F0301c
✅ PL-F0301c - F03.01.13 Shipped
Audit trail on all license changes — PL-F0301c
✅ PL-F0301c - F03.01.14 Shipped
One-license-per-nation-per-season enforcement (system-level constraint) — PL-F0301c
✅ PL-F0301c - F03.01.15 Shipped
Season management (configurable per federation — calendar year, split year, etc.) — PL-F0301c
✅ PL-F0301c - F03.01.16 Shipped
Medical certificate management — configurable per tenant: required at first license (France), renewal cycle (every 3 years), digital attestation sur l'honneur for annual renewal, upload/expiry tracking — PL-F0301d
✅ PL-F0301d - F03.01.17 Shipped
License insurance declaration (licensförsäkran) — allows unlicensed player to participate if club has fulfilled obligations and player signs declaration (Sweden-specific but configurable) — PL-F0301d
✅ PL-F0301d - F03.01.18 Shipped
International license issuance — member can request international license + health certificate for play abroad (required in France etc.) — PL-F0301d
✅ PL-F0301d - F03.01.19 Shipped
Foreign license verification — player with foreign license can participate in national events by presenting valid license from issuing federation — PL-F0301d
✅ PL-F0301d - F03.01.20 Shipped
Unauthorized player enforcement — configurable penalties per tenant (e.g., Sweden: 13-0 forfeit + 5000 SEK fine + exclusion from next year's equivalent event) — PL-F0301d
✅ PL-F0301d - F03.01.21 Shipped
Multi-discipline license support — License.discipline field scopes each license to one discipline (petanque, jeu_provencal, sport_boules, bocce, raffa). Unique index (tenant, license_number, discipline). LicenseType.discipline determines which discipline a license type covers. Auto-propagation: discipline copied from LicenseType to License on creation, auto-added to PlayerProfile.disciplines. Duplicate check scoped by discipline — same player can hold licenses for different disciplines in same season. — PL-T013
✅ PL-T013 - F03.01.22 Shipped
Admin UI för licenshantering — LicenseList med filter (säsong, typ, status, klubb, fritext), massoperationer (godkänn/avslå/påminnelse), CSV-export (UTF-8 BOM), LicenseDetail med audit-tidslinje, bulk-renewal för nästa säsong, massutskick via BulkEmailComposer — PL-T058
✅ PL-T058 - F03.01.23 Shipped
Internt licens-eligibility-söm-kontrakt (§5d.2) — GET /v1/internal/license-eligibility?player=&class=&competition_level=&date= → { eligible, reasons[] } med en fryst skäl-kod-enum (no_active_license, license_expired, wrong_discipline, wrong_class, medical_cert_invalid, transfer_carence_active, license_suspended, license_sanctioned). D-MEMBER är producenten; D-COMP:s anmälningsgrind är konsumenten (m-4 returnerar fakta, D-COMP fattar gate-beslutet). M2M/service-scope (deny-by-default, 403 för icke-intern anropare), tenant-isolerad, tier-gated (403 tenant_type_not_eligible), ren läsning. Inte den publika verifieringen (F03.01.07, D-PUBAPI). — a8fdf771.m-4.t-1
✅ a8fdf771.m-4.t-1 - F03.01.24 Planned
ITC OnApprove-side-effect (cross-tenant nationsbyte, §5d.2) — D-MEMBER registrerar en OnApprove/OnReject-hook för request_type = itc på den delade F1-6-motorn (ingen publik ITC-endpoint). Vid godkännande markeras avgivande licens transferred_out (+ license_event, status_source orörd → åtskild från federation-vägen) och utfärdas exakt en ny nationell licens i mottagande tenant (+ issued-event), idempotent (återspelat beslut → ingen andra licens). D-PUBAPI äger cross-tenant-orkestreringen + det obligatoriska CEP-steget och anropar hooken. Implementerad i api/internal/http/itc_hook.go; blockerad på en acceptans-harness-defekt (push-back till stage 3 — submitApproval saknar If-Match på den generiska F1-6-submit). — a8fdf771.m-4.t-2
⛔ a8fdf771.m-4.t-2 (push-back) - F03.01.27 Shipped
Klubbavgift-ovanpå: frontend-wiring + klubb-tillgänglig licens-enumeration (D-SIGNUP-2) — ny license:read-grindad, node-skopad enumeration GET /v1/tenant/license-types/selectable ({ data:[LicenseType] } med id + insurance_bundled + version) så en node-skopad klubb-admin kan resolva ett license_type_id utan tenant-config.manage (self-guardad, det litterala segmentet vinner över syskonet {id}). De i m-1 byggda ytorna är nu wire:ade till de riktiga endpoints: admin/kom-igang-klubb-avgiftssteget (FeeComposition mode="club") läser selectable→fee-composition och renderar den komponerade totalen FRÅN servern genom den acceptanstestade presentern admin/lib/fee-composition.ts (ingen klient-summa, FR-A5), federations-/assurance-rader read-only, klubbfältet redigerbart → skriver via club-fee (If-Match ur selectable-versionen); 403 autonomy_required → ärlig felrad (FR-D3). admin/medlemsimport (ImportFeeTotal) läser samma total-läsmodell read-only (FR-D2). Gemensam binding admin/lib/club-fee-composition.tsx. Bevisat end-to-end mot riktig Postgres med enbart klubb-inloggning (selectable→compose 350→club-fee→compose 450). — f154e12a.m-2.t-2
✅ f154e12a.m-2.t-2 - F03.01.26 Shipped
Klubbavgift OVANPÅ federationslicensen (D-SIGNUP-2) — komponerad total-läsmodell GET /v1/tenant/license-types/{id}/fee-composition?org_node_id=&season= som vandrar mål-nodens OrgNode-anfäderskedja och returnerar hela nedbrytningen (federationsdel + assurance + ev. distriktsdel(ar) + klubbdel = total, heltals-minor-units + ISO-4217) som EN sanningskälla guiden/importen/admin läser (nod-override vinner över nivå-default; frånvaro = 0; olika valutor → 409 currency_mismatch; ekar ETag). Klubb-skopad skriv-väg PUT /v1/tenant/license-types/{id}/club-fee där en klubb-admin sätter enbart sin egen del: mål-nod server-härledd ur aktörens license:set_club_fee-org-node-grant (aldrig ur body/URL — IDOR-säker), pricing-autonomi-vaktad (403 autonomy_required), scope-vaktad (403 fee_scope_forbidden mot annan nod/federationsdel), If-Match/412 stale_version, idempotent, exakt en audit_log-rad, klubben rör aldrig federationens/distriktets/assurance-del. Ny capability license:set_club_fee på clubadmin. Governing-vägen (GET/PUT …/fees) oförändrad. — f154e12a.m-2.t-1
✅ f154e12a.m-2.t-1 - F03.01.25 Shipped
Reaktiv disciplin-sanktions-söm (D-DISCIPLINE → D-MEMBER, §5d.3) — interna M2M/service-skopade endpoints POST /v1/internal/license-sanctions (apply) + POST /v1/internal/license-sanctions/{license_id}/lift (restore). En inkommande sanktion med category ∈ {suspension, ban} sätter licensen status = active_sanction, status_source = 'discipline_case', sanction_case_ref = <case_id> + license_event('sanctioned'); lift/overturn/expire återställer active (+ sanction_lifted-event). Separat väg från den administrativa BR01.14-spärren (status_source = 'federation', BR01.14): en lift rensar aldrig en kvarstående federations-spärr (NFR-12) och warning/fine är en ren no-op (D-COMP BR04.09). Idempotent + audit-loggad; speglas i license-eligibility (license_sanctioned). D-MEMBER äger inte active_sanction-uppslaget (GET /v1/internal/active-sanctions, D-DISCIPLINE) och driver inte ärendet. Implementerad i api/internal/http/license_sanctions_internal.go. — a8fdf771.m-4.t-3
✅ a8fdf771.m-4.t-3
Club Membership
F03.02- F03.02.01 Shipped
Membership application (player applies to join club) — PL-F0302a
✅ PL-F0302a - F03.02.02 Shipped
Club acceptance/rejection workflow — PL-F0302a
✅ PL-F0302a - F03.02.03 Shipped
Membership categories — configurable per tenant (full, associate, honorary, youth, family, support, etc.) — PL-F0302a
✅ PL-F0302a - F03.02.04 Shipped
Membership fee management per category — PL-F0302a
✅ PL-F0302a - F03.02.05 Shipped
Membership card generation — PL-F0302a
✅ PL-F0302a - F03.02.06 Shipped
Member directory per club (with privacy controls) — PL-F0302a
✅ PL-F0302a - F03.02.07 Shipped
Membership start/end dates and status tracking — status transition history, auto-expire endpoint, activated_at/expired_at timestamps — PL-F0302b
✅ PL-F0302b - F03.02.08 Shipped
License club vs support membership — player has exactly one license club (competition rights) but can be support member in other clubs (no competition rights). System enforces single license club constraint. Categories with grants_competition_rights require is_license_club=true. Player summary endpoint. — PL-F0302b
✅ PL-F0302b - F03.02.09 Shipped
Family membership grouping — FamilyGroup model, family discount for 2nd+ member, family membership overview endpoint — PL-F0302b
✅ PL-F0302b - F03.02.10 Shipped
Automatic membership renewal reminders — bulk reminder endpoint with max_reminders threshold, per-club filtering, reminder count tracking — PL-F0302b
✅ PL-F0302b - F03.02.11 Shipped
Club-bound competition enforcement — enhanced validation with expected club_id, per-player club mapping, club name resolution — PL-F0302b
✅ PL-F0302b - F03.02.12 Shipped
ClubInquiry inbox — shared inbox for prospect/visitor inquiries, status-FSM new/assigned/awaiting_reply/responded/converted/closed_no_response/spam, indices för (tenant_id, club_id, status) + (tenant_id, assigned_to, status) + (follow_up_due) — PL-T222
✅ PL-T222 - F03.02.13 Shipped
InquiryAssignment — event-log per (re-)assignment, role-gated på role:club_secretary / role:membership_secretary, dispatchar inquiry.assigned-notification via F09.02 — PL-T222
✅ PL-T222 - F03.02.14 Shipped
InquiryResponseTemplate — Jinja-style {{contact.name}}-substitution, per (club_id, inquiry_type, language)-default, okända variabler collapsar till tomt — PL-T222
✅ PL-T222 - F03.02.15 Shipped
Inquiry follow-up reminder — 5-dagars default-SLA, inquiry_follow_up_tick (1 h-cron), single-fire via follow_up_reminder_sent_at-marker — PL-T222
✅ PL-T222 - F03.02.16 Shipped
Convert-to-member — skapar ClubMember från inquiry, sätter inquiry.converted_to_member_id + status=converted, 409 vid email-kollision — PL-T222
✅ PL-T222 - F03.02.17 Shipped
Public CMS inquiry-form-endpoint — POST /public/clubs/{id}/inquiries med rate-limit 10/h per IP, honeypot-fält website (left:-9999px), 5-min dedup på (email, subject), email lower-normaliserad — PL-T222
✅ PL-T222
Transfers
F03.03### Transfer Rules — Vary Per Nation
How it works- F03.03.01 Shipped
Intra-national club transfer (OrgNode change within same tenant) — PL-F0303a
✅ PL-F0303a - F03.03.02 Shipped
International transfer (cross-tenant, nation to nation via public APIs) — PL-F0303a
✅ PL-F0303a - F03.03.03 Shipped
Configurable transfer window per tenant — open period (Germany: Nov-Dec), free period before deadline (Sweden), or always open (Denmark). Tenant admin configures. — PL-F0303a
✅ PL-F0303a - F03.03.04 Shipped
Configurable transfer approval workflow per tenant — who must approve (player, old club, new club, federation, district/region). Each step configurable. — PL-F0303a
✅ PL-F0303a - F03.03.05 Shipped
Max transfers per season — configurable limit per tenant (Sweden: 2, others: unlimited) — PL-F0303a
✅ PL-F0303a - F03.03.06 Shipped
Transfer quarantine during active league — configurable: player cannot transfer while league season is active (Denmark Landsturneringen). Tenant-scoped league check. — PL-F0303b
✅ PL-F0303b - F03.03.07 Shipped
Cross-region/cross-district transfer — additional approval required when transferring between OrgNode subtrees (Spain: autonomous federation approval, France: inter-comité mutation). Configurable approval type per tenant. — PL-F0303b
✅ PL-F0303b - F03.03.08 Shipped
Outstanding obligations check — system blocks transfer if player has unpaid fees/active sanctions. Configurable blocking period (Sweden: max 2 years). Sanctions always checked. — PL-F0303b
✅ PL-F0303b - F03.03.09 Shipped
Carta de libertad / release letter — some nations require formal release document from departing club. Configurable per tenant. Submit/verify/reject workflow. — PL-F0303b
✅ PL-F0303b - F03.03.10 Shipped
ITC generation and verification via public API (peer-to-peer between tenants). Verify endpoint validates authenticity and status. — PL-F0303b
✅ PL-F0303b - F03.03.11 Shipped
ITC workflow: player requests in new nation → new nation verifies license status via public API → sends ITC request → old nation reviews and approves/rejects → license marked "transferred out" → new nation issues license — PL-F0303c
✅ PL-F0303c - F03.03.12 Shipped
Transfer fee handling — configurable per tenant (who pays, amount) — PL-F0303c
✅ PL-F0303c - F03.03.13 Shipped
Transfer history per player (all clubs, all nations, all seasons) — PL-F0303c
✅ PL-F0303c - F03.03.14 Shipped
One-nation-per-season validation on international transfer — PL-F0303c
✅ PL-F0303c - F03.03.15 Shipped
Post-transfer competition eligibility delay — configurable per tenant (France: délai de carence ~30 days after mutation before player is competition-eligible in new club). Distinct from transfer quarantine (F03.03.06). — PL-F0303c
✅ PL-F0303c - F03.03.16 Shipped
Transfer refusal appeal/escalation — when a step in the transfer workflow is rejected (e.g., old club refuses), player can escalate to higher OrgNode level (France: appeal to comité départemental). Configurable escalation path per tenant. — PL-F0303d
✅ PL-F0303d - F03.03.17 Shipped
Transfer exception conditions — configurable per tenant: allow transfer outside normal window if specific conditions are met (Germany: Wohnsitzwechsel/residence change). Exception must be documented and approved by federation. — PL-F0303d
✅ PL-F0303d - F03.03.18 Shipped
Multi-club membership toggle — configurable per tenant: some nations allow support membership in multiple clubs (Sweden), others forbid all multi-club affiliation (France: double appartenance interdit). — PL-F0303d
✅ PL-F0303d - F03.03.19 Shipped
Audit trail on all transfers — embedded TransferAuditEntry list with action, performer, timestamp, status transitions. Audit endpoint GET /transfers/{id}/audit. — PL-F0303d
✅ PL-F0303d
Federation Affiliation
F03.04- F03.04.01 Shipped
Club affiliation to regional/national federation — PL-F0304a
✅ PL-F0304a - F03.04.02 Shipped
Annual affiliation renewal — PL-F0304a
✅ PL-F0304a - F03.04.03 Shipped
Affiliation fee calculation (based on member count) — PL-F0304a
✅ PL-F0304a - F03.04.04 Shipped
Affiliation document management (statutes, insurance proof, etc.) — PL-F0304a
✅ PL-F0304a - F03.04.05 Shipped
New federation/club application process — AffiliationApplication model with full workflow (draft → submitted → under_review → approved/rejected). Club applications auto-create Club entity on approval. CRUD + status filtering, parent org validation, audit trail. — PL-F0304b
✅ PL-F0304b - F03.04.06 Shipped
Affiliation status (active, suspended, withdrawn) — Enhanced suspend/reinstate/terminate endpoints with 409 conflict codes, structured audit logging. Proper status transition enforcement. — PL-F0304b
✅ PL-F0304b - F03.04.07 Shipped
Federation member count reporting (upward to parent) — MemberCountReport model with club/district/federation-level aggregation. Counts active members and licensed members. Persisted snapshots for FIPJP/CEP annual reporting. — PL-F0304b
✅ PL-F0304b
Insurance
F03.05- F03.05.01 Shipped
Insurance integration with license (mandatory liability) — InsurancePolicy model tied to License with auto-generated policy numbers, per-tenant provider config, coverage amounts, group policy linkage. Tenant InsuranceConfig extended with coverage_type, default_coverage_amount, provider_contact_email, provider_api_endpoint — PL-F0305
✅ PL-F0305 - F03.05.02 Shipped
Optional insurance add-ons (equipment, travel, accident) — InsuranceAddonSubscription model with addon_code validated against tenant's InsuranceConfig.optional_addons, premium tracking from InsuranceAddon.premium_amount, subscribe/cancel workflow, one active subscription per player/addon/season — PL-F0305
✅ PL-F0305 - F03.05.03 Shipped
Insurance certificate generation — Enhanced /insurance/certificate/{player_id}/{season_id} with policy_number, addon_coverages list, group_policy_number, coverage_amount from policy or tenant default. Comprehensive certificate data for PDF rendering — PL-F0305
✅ PL-F0305 - F03.05.04 Shipped
Claim submission workflow — InsuranceClaim model with full status workflow (submitted→under_review→approved/rejected→paid), ClaimDocument attachments, ClaimStatusChange audit trail, per-tenant claims_enabled gating, valid transition enforcement (409 on invalid), review endpoint with approved_amount/paid_amount/provider_claim_reference — PL-F0305
✅ PL-F0305 - F03.05.05 Shipped
Insurance provider integration (API/file exchange) — InsuranceProviderExport model tracking batch exports (policies/claims/renewals/cancellations) in csv/xml/json, export generation with record counting, provider acknowledgement workflow, file_url generation, provider_reference tracking — PL-F0305
✅ PL-F0305 - F03.05.06 Shipped
Group insurance management for clubs/federations — GroupInsurancePolicy model for club/district/federation-level policies, member count tracking (auto-incremented when individual policies linked), coverage per member, renewal management, member listing endpoint, per-tenant group_policies_enabled gating — PL-F0305
✅ PL-F0305
Per-nationella licensregler
F03.06- F03.06.01 Shipped
France — 4 licenstyper (Compétition, Loisir, Jeune, Découverte) — Konfigurerbara via LicenseType-modellen med allows_competition, time_limited/max_duration_days (90d för découverte), one_time_only, max_age (17 för jeune), requires_medical_certificate med 3-årscykel, includes_insurance. Seed-template i NATIONAL_TEMPLATES["FR"]. 53 tester inkl. France-fixture — PL-F0306a
✅ PL-F0306a - F03.06.02 Shipped
France — ligue+comité+club godkännandekedja — LicenseRuleConfig.approval_chain med 3 steg: comité départemental (org_level 1) → ligue régionale (org_level 0) → FFPJP (org_level -1). ApprovalChainRecord spårar varje steg med status/reviewer/timestamp/kommentar. Kedjan initieras vid submit, steg måste godkännas i ordning, avslag i vilket steg som helst avbryter hela ansökan. Fullständig chain-approve endpoint /license-applications/{id}/approval-chain/{step}/approve
reject — PL-F0306a | ✅ PL-F0306a - F03.06.03 Shipped
Sverige — en licenstyp utfärdad av klubben — LicenseRuleConfig med issuer_type="club", tom approval_chain, max_license_types=1. Ansökan går direkt till "submitted" utan godkännandekedja. requires_insurance_declaration=True (licensförsäkran). Seed-template NATIONAL_TEMPLATES["SE"] — PL-F0306a
✅ PL-F0306a - F03.06.04 Shipped
Deutschland — DPV-licens med Landesverband-godkännande — LicenseRuleConfig med issuer_type="org_node", issuer_org_level=0 (Landesverband), 1-stegs approval_chain (org_level 0). Verein ansöker → Landesverband godkänner → licens skapas. Seed-template NATIONAL_TEMPLATES["DE"] — PL-F0306a
✅ PL-F0306a - F03.06.05 Shipped
Norge — inget licenssystem — LicenseRuleConfig med competition_requires_license=False, membership_only_competitions=True. Rekryteringsturneringar (LF) kräver bara medlemskap, ranking/NM kräver lisens. validate_competition_entry_allowed() hanterar båda fallen. Competition entry check endpoint — PL-F0306a
✅ PL-F0306a - F03.06.06 Shipped
Espana — FEP-licens med autonom region-koppling — LicenseRuleConfig med autonomous_regions_manage_licenses=True, license_photo_required=True, autonomous_region_stamp_required=True. 1-stegs approval av federación autonómica (org_level 0). 3 licenstyper (deportiva, técnica, árbitro). Seed-template NATIONAL_TEMPLATES["ES"] — PL-F0306a
✅ PL-F0306a - F03.06.07 Shipped
Danmark — DPF-licens — NATIONAL_TEMPLATES["DK"] med issuer_type="federation", tom approval_chain, max_license_types=1, flat struktur (org_hierarchy_depth=0). 1 licenstyp (standard) med base_fee=350 DKK. Inget medicinsk intyg-krav. Competition requires license. — PL-F0306b
✅ PL-F0306b - F03.06.08 Shipped
FIPJP — ingen direktlicens — NATIONAL_TEMPLATES["FIPJP"] med license_system_enabled=False, verification_only=True. Världsförbundet utfärdar inga licenser — verifierar nationella licenser vid VM/World Games. LicenseRuleConfig utökad med verification_only-fält och validator. — PL-F0306b
✅ PL-F0306b - F03.06.09 Shipped
CEP — kontinentala spelartillstånd — NATIONAL_TEMPLATES["CEP"] med continental_permit_enabled=True, continental_permit_validity_days=30, continental_permit_requires_national_license=True. Utfärdar tidsbegränsade tillstånd (30 dagar) för EM-deltagande. continental_permit-licenstyp. POST /license-rules/continental-permit-check validerar att spelaren har aktiv nationell licens. LicenseRuleConfig utökad med continental_permit_*-fält och validators. — PL-F0306b
✅ PL-F0306b - F03.06.10 Shipped
Per-tenant pris och giltighetstid — LicenseType utökad med base_fee (Decimal), fee_currency (ISO 4217), reduced_fee_pct (rabatt%). calculate_license_fee() beräknar effective_fee med rabatt. calculate_license_validity() beräknar giltighetsperiod (season vs fixed_days). GET /license-rules/fee-info/{id} returnerar avgifts- och giltighetsdetaljer. Alla nationella templates uppdaterade med fee-data (FR: 80€ competition/35€ loisir/20€ jeune/5€ découverte, SE: 300 SEK, DE: 40€, NO: 400 NOK, DK: 350 DKK). — PL-F0306b
✅ PL-F0306b - F03.06.11 Shipped
Per-tenant medicinska intyg-krav — validate_medical_certificate_requirement() kombinerar LicenseType.requires_medical_certificate med TenantConfig.medical_certificate_config (required, renewal_cycle_years, allows_self_declaration). Returnerar required/valid/reason/self_declaration_allowed/next_certificate_due. Alla nationella templates inkluderar medical_certificate_config. POST /license-rules/medical-certificate-check endpoint. Frankrike: 3-årscykel med självdeklaration. Sverige/DK/DE/NO: ej krav. — PL-F0306b
✅ PL-F0306b - F03.06.12 Shipped
Per-tenant ungdomslicens-regler (under 18) — LicenseType utökad med requires_parental_consent (bool, kräver max_age), reduced_fee_pct (rabatt%, kräver base_fee). validate_youth_license_eligibility() kontrollerar ålder (min_age/max_age), föräldrasamtycke, returnerar eligible/reason/player_age. POST /license-rules/youth-eligibility-check endpoint. Frankrike licence jeune: max_age=17, requires_parental_consent=True, reduced_fee_pct=75 (75% rabatt). Modell-validators: parental_consent kräver max_age, reduced_fee kräver base_fee. — PL-F0306b
✅ PL-F0306b
Spelartransfer mellan klubbar och nationer
F03.07### Transfer Rules — Vary Per Nation
How it works- F03.07.01 Shipped
Per-tenant transferfönster (datum) — Konfigurerbart via TransferConfig.window_type (always_open / deadline / strict_period), window_start/window_end (MonthDay) för strict_period, deadline (MonthDay) för deadline-typ. Stöder årsövergripande fönster (t.ex. Nov–Jan). Undantagsvillkor (exception_conditions) kringgår fönsterrestriktion. Verifierat med 9 integrationstester (always_open, strict_period in/out/wrap, deadline before/after, exception override, per-tenant isolation) — PL-F0307a
✅ PL-F0307a - F03.07.02 Shipped
France — 1 transfer per säsong — TransferConfig.max_per_season=1 för franska federationen. Begränsningen är per spelare per säsong. Cancelled/rejected transfers räknas inte mot gränsen. Annan spelare i samma säsong påverkas inte. Verifierat med 6 integrationstester (first allowed, second blocked, cancelled/rejected don't count, config stored, per-player isolation) — PL-F0307a
✅ PL-F0307a - F03.07.03 Shipped
Sverige — fri transfer utan fönster — TransferConfig.window_type="always_open" utan max_per_season-begränsning. Ingen competition eligibility delay. Spelare kan genomföra flera godkända transfers under samma säsong. Verifierat med 5 integrationstester (always_open config, no max, transfer succeeds, multiple allowed, no delay) — PL-F0307a
✅ PL-F0307a - F03.07.04 Shipped
Avgivande klubb-godkännande — old_club i required_approvals-kedjan. Godkännande avancerar workflow till nästa steg (pending_new_club). Avslag terminerar transfern (status=rejected). Registrerar approver user ID, beslutsdatum och kommentar. Konfigurerbart — kan utelämnas i tenants som inte kräver det. Verifierat med 5 integrationstester (in chain, approve advances, reject blocks, optional in minimal config, user ID recording) — PL-F0307a
✅ PL-F0307a - F03.07.05 Shipped
Mottagande klubb-godkännande — new_club i required_approvals-kedjan. I Sverige: sista steget → transfer godkänns, gammal ClubMembership→EXPIRED, ny ACTIVE skapad, License.club_id uppdaterat. I Frankrike: avancerar till pending_federation. Avslag terminerar transfern. Verifierat med 6 integrationstester (in chain, advances to federation in FR, final step in SE, reject blocks, membership updated, license updated) — PL-F0307a
✅ PL-F0307a - F03.07.06 Shipped
Federation-godkännande för internationell transfer — TransferConfig.itc_federation_approval_required (opt-in, default false). När aktivt: ITC startar i pending_federation_approval-status. PATCH /itc/{itc_number}/federation-approve godkänner/avvisar internt. Godkännande → pending_release. Avslag → rejected. Release-endpoint blockeras medan federation-godkännande saknas. Audit trail inkluderar federation_approved/federation_rejected. Verifierat med 7 integrationstester (approval required/not required, approve advances, reject blocks, release blocked, double approve rejected, audit trail) — PL-F0307b
✅ PL-F0307b - F03.07.07 Shipped
Karantänsperiod för internationell transfer — TransferConfig.itc_competition_eligibility_delay_days (t.ex. France: 30 dagar, Deutschland: 15 dagar). Sätts vid ITC-komplettering baserat på MOTTAGANDE federationens config. competition_eligible_from datum lagras på ITC. GET /itc/{itc_number}/eligibility kontrollerar tävlingsbehörighet med valfritt check_date. Verifierat med 4 integrationstester (quarantine set, no quarantine, during quarantine, after quarantine) — PL-F0307b
✅ PL-F0307b - F03.07.08 Shipped
ITC via FIPJP (interkontinentala transfers) — Konfederationsmappning för ~50 länder (CEP, COPAB, CAP, CAPP). is_intercontinental auto-detekteras när konfederationer skiljer sig. Interkontinentala transfers: release → pending_fipjp istället för released. PATCH /itc/{itc_number}/fipjp-approve — FIPJP godkänner/avvisar med referensnummer. Kontinentala (samma konfederation) transfers släpps direkt. Verifierat med 8 integrationstester (confederation lookup, intercontinental detection, FIPJP required, approve/reject, non-intercontinental blocked, continental skip) — PL-F0307b
✅ PL-F0307b - F03.07.09 Shipped
Transferhistorik och spårbarhet — GET /itc/history/{player_id} med komplett ITC-historik inkl. alla spårbarhetsfält (is_intercontinental, confederation_code, federation_approval_status, fipjp_reference, competition_eligible_from, fee). Valfri include_audit=true för full audit trail per ITC. GET /itc/{itc_number}/audit för dedikerad audit trail. Enhetlig GET /transfers/history/{player_id} utökad med ITC-spårbarhetsfält. Verifierat med 6 integrationstester (history, traceability fields, audit trail, unified history, pagination) — PL-F0307b
✅ PL-F0307b - F03.07.10 Shipped
Avgift per transfer (per tenant konfigurerbar) — TransferConfig.itc_fee_amount/itc_fee_currency/itc_fee_payer. Avgift fångas från avgivande federationens config vid ITC-förfrågan. PATCH /itc/{itc_number}/fee/pay markerar betald. GET /itc/{itc_number}/fee returnerar avgiftsdetaljer. FIPJP förbjuder avgifter på själva ITC-certifikatet men nationella förbund kan ta interna handläggningsavgifter. Verifierat med 7 integrationstester (fee captured, no fee, mark paid, no fee 400, idempotent, audit trail, DE config) — PL-F0307b
✅ PL-F0307b
Global Player Passport (FIPJP)
F03.08Cross-tenant spelaridentitet i FIPJP-tenanten. Kopplar auth-identity till alla nationella licenser.
How it works- F03.08.01 Shipped
GlobalPassport datamodell — datamodell med passport_id (FIPJP-YYYY-NNNNNN), auth_identity_id (unik), personuppgifter, linked_licenses (embedded lista med tenant_code, license_number, season, status, last_verified_at). Tenant-scoped till FIPJP. — PL-T009
✅ PL-T009 - F03.08.02 Shipped
FIPJPTenantConfig — Singleton per FIPJP-tenant: passport_number_format, next_passport_sequence (atomärt via $inc), photo_required, photo_max_size_kb. — PL-T009
✅ PL-T009 - F03.08.03 Shipped
FIPJP admin endpoints — POST /fipjp/passports (skapa), GET /fipjp/passports/{id} (hämta), POST /link-license (koppla licens), POST /verify-license (verifiera mot nationell tenant, 24h cache), PATCH /status (suspendera/återkalla). — PL-T009
✅ PL-T009 - F03.08.04 Shipped
Publika verifierings-endpoints — GET /public/passports/{id} (privacy-safe: namn, nationalitet, licensstatus — ingen DoB/foto), GET /public/passports/by-license?tenant_code&license_number (lookup). — PL-T009
✅ PL-T009 - F03.08.05 Shipped
Automatisk onboarding — passport_sync.py: link_local_license_to_passport() skapar passport vid första licens, lägger till licens vid ytterligare. Fire-and-forget (licensutfärdande misslyckas inte vid sync-fel). — PL-T009
✅ PL-T009 - F03.08.06 Shipped
TenantConfig passport sync — fipjp_passport_sync_enabled (default true), fipjp_passport_sync_endpoint, fipjp_m2m_credentials_secret_ref på alla national tenants. — PL-T009
✅ PL-T009 - F03.08.07 Shipped
Backfill-jobb — backfill_global_passports(): itererar alla aktiva licenser över alla national tenants, skapar passport per auth_identity, länka licens. Idempotent. — PL-T009
✅ PL-T009 - F03.08.08 Shipped
Cross-tenant suspension-koppling till passport — CrossTenantSuspensionEntry.subject_passport_id länkar varje global suspension till sitt FIPJP-pass. Subscriber-tenants matchar på subject_passport_id (O(1)) först, sen fallback på subject_full_name_normalized + birth_year när passport saknas. Aktiv ban blockerar licens-issuance och event-inscription via LocalSuspensionCache-uppslag. Se docs/domain/discipline/cross-tenant-suspension.md. — PL-T213
✅ PL-T213
No-touch medlemsimport (claim mot verifierad identifierare)
F03.09No-touch medlemsimport (D-SIGNUP-2 m-4): en federation/klubb importerar sitt medlemsregister
- F03.09.01 Shipped
Import-batch + sidoeffektsfri preview — POST /v1/tenant/member-imports (redan-normaliserade rader + match-nyckel-policy) och GET …/{id}/preview (summary-tal + per-rad-status, inga sidoeffekter). Capability member_import:manage, tenant server-härledd. — f154e12a.m-4
✅ f154e12a.m-4 - F03.09.02 Shipped
Match mot verifierad identifierare — licens auto-länkas ENDAST mot email_verified=true; overifierad/okänd/mobil-endast → pending claim; dubbletter → attention. Ingen konto-enumeration mot importören. — f154e12a.m-4
✅ f154e12a.m-4 - F03.09.03 Shipped
Idempotent commit + konsent — POST …/{id}/commit (If-Match/stale_version, Idempotency-Key, 409 import_has_conflicts): licens auto-länk vs stödmedlem pending invite. — f154e12a.m-4
✅ f154e12a.m-4 - F03.09.04 Shipped
Pending claims + attach-på-verifiering — member_pending_claim-ledger; registrerad side-effect på F1-3-verifiering attach:ar matchande claims (idempotent per claim/identitet). — f154e12a.m-4
✅ f154e12a.m-4 - F03.09.05 Shipped
Spelar-yta — GET /v1/me/imported-records (med källa), POST …/{id}/dispute (bestrid licens), POST /v1/me/membership-invites/{id}/accept (aktivera stödmedlemskap). — f154e12a.m-4
✅ f154e12a.m-4 - F03.09.06 Shipped
Avgifts-snapshot + full audit — fryst komponerad total (*_minor bigint, läser m-2) med fee_source; audit-kedja batch → rad → (claim\
attach) → licens/medlemskap. — f154e12a.m-4 | ✅ f154e12a.m-4 - F03.09.08 Shipped
Kontraktsintegritet: spec-giltig input får aldrig 500 (D-STRUKT §3) — attach-obligatoriska fält (licens: license_type_id+org_node_id; stödmedlem: org_node_id+membership_category_id) klassas som attention vid resolution (delad av preview+commit) FÖRE varje skrivning, med maskinläsbar orsak i preview-svaret (reason ∈ {duplicate_identifier, missing_required_fields} + missing_fields[], additivt). En ofullständig rad blir aldrig ett member_pending_claim; commit av en attention-batch svarar kontrakterat 409 import_has_conflicts (svensk text som skiljer dubbletter från saknade fält) — aldrig omappad 500. Accept av ett ofullständigt legacy-claim → mappad 409 import_claim_incomplete (claimet oförändrat, ingen club_membership). Attach-på-verifiering hoppar över förgiftade claims (slog-varning, korrelerad) i stället för att rulla tillbaka hela batchen; hookens fel loggas. Omappade fel loggas server-side (slog ERROR + request_id, generiskt svar). — 35728185.m-1.t-1
✅ 35728185.m-1.t-1 - F03.09.07 Shipped
Frontend-wiring: admin-skärm + spelarens app-vy mot riktig data — admin/medlemsimport kör hela flödet mot m-4:s endpoints (roster → match-nyckel-väljare → skapa batch + sidoeffektsfri preview → summary-tal + preview-tabell med badges linked/pending/attention FRÅN servern → idempotent/optimistiskt-låst commit; dubbletter blockerar; empty/loading/error-states; konsent-sektion; fee-total från m-2; "← Tillbaka till setup" → /kom-igang). Binding admin/lib/member-import.tsx via genererad klient (memberImportOps). Spelarens app/me/importerade-poster (app/lib/imported-records.tsx) listar GET /v1/me/imported-records med källa och driver bestrid (licens) + acceptera (stödmedlem). Inga hårdkodade siffror. Specs: ../../../specs/admin/views/medlemsimport.md, ../../../specs/app/views/imported-records.md. — f154e12a.m-4.t-2
✅ f154e12a.m-4.t-2
Fri-form affiliate-medlemshantering (D-AFFILIATE)
F03.10Affiliate-organisationer (Korpen/SPF Seniorerna/Handisport/Skolidrott) hanterar sina **egna**
- F03.10.01 Shipped
Fri-form kategorikatalog — GET/POST /v1/tenant/affiliate-membership-categories, GET/PUT …/{id}, POST …/{id}/retire. Speglar D-MEMBER-katalogens konventioner (tenant ur bearer, {data:[…]}, ETag/If-Match 412 stale_version, mjuk pensionering 409 category_in_use, audit i samma transaktion) med spegel-tier-gate endast tenant_type=AFFILIATE (övriga 403 tenant_type_not_eligible). Fält: code (unik/tenant), name_i18n, fee_minor_units (heltal ≥ 0), currency, age_min/age_max (informativa), metadata_schema ∈ {none, parasport_classification, minor_consent, club_affiliation}. Medvetet frånvarande: tävlingsrätt/licens/BR01.13/OrgNode-gate. — b1a09359.m-2.t-1
✅ b1a09359.m-2.t-1 - F03.10.02 Shipped
Fri-form medlemsregister — GET/POST /v1/tenant/affiliate-members, GET/PUT …/{id}. Filter q/category_id/status, paginering (default 50, tak 500) med meta.total. Fält: first_name/last_name/category_id (obligatoriska), email/phone/birth_year/gender/member_since, status active\
dormant|ended, payment_status paid|pending|exempt (ren bokföringsflagga, ingen pengarörelse), metadata validerad mot kategorins schema, nullbar read-only auth_identity_id (F1-3-förberedelse). — b1a09359.m-2.t-1 | ✅ b1a09359.m-2.t-1 - F03.10.03 Shipped
Default-kategoriseed ur organisation_type — idempotent, lat seed vid första kategori-LIST (per-tenant advisory-lås, noll-rader-villkor skyddar egna val): sports_movement→Motion/Tävling/Prova-på; senior_association→Aktiv boule/Motion/Prova-på; parasport→Sittande/Stående/Prova-på; school_sports→Grundskola/Gymnasium. Avgift 0 i tenantens valuta. Täcker även backfyllnad av m-1-provisionerade affiliates. — b1a09359.m-2.t-1
✅ b1a09359.m-2.t-1 - F03.10.04 Shipped
Kapabilitets-nåbarhet + kontraktsexponering — den m-1-provisionerade affiliate-ägaren (tenant_membership role=owner, utan role_assignment) får member:read+membership:manage deriverade i authz-resolvern, hårt scopad till tenant_type=affiliate (federation-ägare får inget). GET /v1/me/tenants bär tenant_type + organisation_type (openapi-deklarerat, TS-klient regenererad) så admin kan välja yta. — b1a09359.m-2.t-1
✅ b1a09359.m-2.t-1 - F03.10.05 Shipped
Admin-ytval per tenant-typ — admin/app/medlemmar grenar på tenant_type ur GET /v1/me/tenants: affiliate-tenant → affiliate-registret; governing-tenant → oförändrat D-USERS-registret. En affiliate-admin landar aldrig på governing-ytan som 403:ar (tenant_type_not_eligible som råkod i UI = defekt). — b1a09359.m-2.t-2
✅ b1a09359.m-2.t-2 - F03.10.06 Shipped
Admin: affiliate-registret — kontextrad "Affiliate · <org>", kapabilitets-chips ur tenant-typ-fakta (✓ Medlemshantering · ✓ Interna tävlingar · ✕ Officiella licenser · ✕ Sanktionera rankat, konsistent med tier_matrix.go TypeAffiliate), ⚙ Kategorier / + Ny medlem, filter (sök namn/kategori/status), tabell Medlem·Kategori·Status, paginering "Visar x–y av N", fyra verkliga states (happy/laddning/tomt/behörighet member:read). — b1a09359.m-2.t-2
✅ b1a09359.m-2.t-2 - F03.10.07 Shipped
Admin: kategorivyn — skiljelinje-panel mot governing-katalogen, filter Alla/Aktiva/Pensionerade, listtabell (Namn+beskrivning·Kod·Avgift·Ålder·Status), skapa/redigera-formulär med FR-1-fälten + hjälptexter (unik kod, fee_minor_units heltal — 0 tillåts, valfria åldersgränser, metadata_schema), kontrast-panel (ingen tävlingsrätt/licens/OrgNode-gate), mjuk pensionering med bekräftelse + klarspråk vid 409 category_in_use. — b1a09359.m-2.t-2
✅ b1a09359.m-2.t-2 - F03.10.08 Shipped
Admin: medlemsdetaljen + Ny medlem — Översikt (grunddata inkl. read-only global identitet) + Kategori & metadata (kategoriväljare ur aktiva kategorier, ärvd avgift, betalstatus Betald/Väntar/Befriad, undertyps-metadata-fält renderade per den valda kategorins metadata_schema och nollställda vid kategoribyte så inget off-schema-fält skickas), redigera/spara med If-Match + klarspråk vid 412 stale_version, + Ny medlem med skicka-grind. Alla fel i klarspråk (å/ä/ö), aldrig råa problem-koder. Utelämnat (m-3, ej stubbat): bridge-/konsoliderad historik-flikar, register-import. — b1a09359.m-2.t-2
✅ b1a09359.m-2.t-2
No features match your filters.