Per-nationella licensregler
At a glance
Per-nation license rules express each federation's specific licensing model — number of license types, issuer level, multi-step approval chains, fees and currencies, validity periods, medical-certificate requirements with renewal cycles and self-declaration, youth rules with parental consent and reduced fees, and verification-only modes — entirely through configuration via the LicenseRuleConfig and LicenseType models, with seed templates pre-loaded for FR, SE, DE, NO, ES, DK, FIPJP, and CEP.
How it works
The platform encodes national licensing variation entirely in configuration rather than code. Each tenant has a LicenseRuleConfig that defines issuer_type (federation, club, or org_node), issuer_org_level for org_node issuers, approval_chain (an ordered list of approval steps with org_level), max_license_types, and competition_requires_license. The LicenseType model layers per-type configuration: allows_competition, time_limited / max_duration_days, one_time_only, max_age, requires_medical_certificate, requires_parental_consent, includes_insurance, base_fee, fee_currency, and reduced_fee_pct. National templates in NATIONAL_TEMPLATES seed each tenant with the correct combination at provisioning time.
France has four license types — Compétition (80 EUR), Loisir (35 EUR), Jeune (max_age 17, 75 percent reduced fee, parental consent required), and Découverte (90 days, one-time only, 5 EUR) — and a three-step approval chain: comité départemental (org_level 1) → ligue régionale (org_level 0) → FFPJP (org_level -1), each step tracked through ApprovalChainRecord with status, reviewer, timestamp, and comment. Sweden runs a single license type at 300 SEK issued directly by the club (issuer_type=club, empty approval chain) plus a licensförsäkran self-declaration that lets unlicensed players participate when the club has fulfilled obligations. Germany uses Landesverband-level issuance (org_level 0) with a single-step approval chain. Norway has no license system — competition_requires_license=False with membership_only_competitions=True for recruitment tournaments (LF), but ranking and NM still require a license. Spain assigns licenses through autonomous regions with photo and regional stamp required, three license types, and one-step regional approval. Denmark is flat (org_hierarchy_depth=0) with federation-issued licenses at 350 DKK.
FIPJP itself issues no licenses — license_system_enabled=False with verification_only=True; it verifies national licenses at world championships. CEP issues continental permits (30 days) for European championship participation, gated by an active national license through POST /license-rules/continental-permit-check. Per-tenant fee and validity calculation runs through calculate_license_fee() (applying reduced_fee_pct) and calculate_license_validity() (season vs fixed_days). Medical certificate validation combines LicenseType.requires_medical_certificate with TenantConfig.medical_certificate_config (renewal_cycle_years, allows_self_declaration); France enforces a three-year cycle with annual self-declaration. Youth eligibility validation checks min_age/max_age, parental consent, and applies youth fee discounts.
Key capabilities
- LicenseRuleConfig + LicenseType express any national licensing model in configuration
- Seed templates for FR, SE, DE, NO, ES, DK, FIPJP, CEP via NATIONAL_TEMPLATES
- Multi-step approval chains with ApprovalChainRecord per step (France 3-step, Germany/Spain 1-step)
- Per-tenant fees and validity via calculate_license_fee() and calculate_license_validity()
- Medical-certificate validation with renewal cycles and self-declaration support
- Youth license eligibility check with parental consent and reduced fee
- FIPJP verification-only mode and CEP continental permit (30 days) gated on national license
In practice
A 16-year-old French player applies for a licence Jeune through her club. The club's secretary opens the admin license-application form; the system reads the FFPJP LicenseRuleConfig and renders the four French license types. Selecting Jeune triggers the youth-eligibility check (max_age=17 satisfied, parental consent required) and surfaces a parental consent capture form.
Fee calculation applies the 75 percent reduction to the base 80 EUR Compétition fee, producing 20 EUR. Medical certificate validation determines the player needs an initial certificate (no prior license). On submit, the application enters the approval chain: comité 13 reviews and approves, ligue PACA approves, FFPJP approves — each step writing an ApprovalChainRecord.
The license is issued, an InsurancePolicy is created automatically, and a digital card is delivered.
Features in this subsystem
12| ID | Status | Features |
|---|---|---|
| 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 |