Skip to main content
Petanque Life
← Back to all features
08

Finance & Billing

246 features · 20 subsystems

Financial management across all federation levels. Leverages Craft-Easy's financial primitives (payments, invoicing, billing, bookkeeping) extended with petanque-specific financial workflows.

Fee Management

F08.01
Platform+
How it works
  • F08.01.01 Shipped

    Composite license fee configuration — license fee = sum of configurable components set at different OrgNode levels. Example France: part fédérale (set by FFPJP) + part ligue (set by ligue régionale) + part comité (set by comité départemental) + assurance. Each component configurable per license type, age, category. Supports regional price variation (France, Spain).

    ✅ PL-F0801a
  • F08.01.02 Shipped

    Club affiliation fee configuration (flat or per-member)

    ✅ PL-F0801a
  • F08.01.03 Shipped

    Competition entry fee management

    ✅ PL-F0801a
  • F08.01.04 Shipped

    Fee schedules per season/year

    ✅ PL-F0801a
  • F08.01.05 Shipped

    Early bird / late fee surcharges

    ✅ PL-F0801a
  • F08.01.06 Shipped

    Fee waivers and discounts (youth, veteran, disabled, family) — configurable per tenant (e.g., Sweden: no sanction fee for youth classes)

    ✅ PL-F0801b
  • F08.01.07 Shipped

    Fee splitting with automatic distribution — when a license is purchased, the system automatically splits the payment to the correct recipients (federation account, regional account, district account, insurance). Configurable split ratios per OrgNode level.

    ✅ PL-F0801b
  • F08.01.08 Shipped

    Fee history and audit trail

    ✅ PL-F0801b
  • F08.01.09 Shipped

    Sanction fee per competition class — configurable per tenant (e.g., Sweden: 300 SEK per class, paid to district where competition is held). Supports different fee rules per competition level.

    ✅ PL-F0801b
  • F08.01.10 Shipped

    License suspension administrative fee — configurable per tenant (e.g., Sweden: 2× senior license fee, charged to the club requesting suspension)

    ✅ PL-F0801b

Payment Processing

F08.02
Platform+
How it works
  • F08.02.01 Shipped

    ✅ Payment recording and status tracking

  • F08.02.02 Planned

    ✅ Online payment gateway integration (Stripe, PayPal, local providers)

  • F08.02.03 Planned

    ✅ Bank transfer payment matching

  • F08.02.04 Shipped

    ✅ Payment receipt generation

  • F08.02.05 Planned

    ✅ Installment payment plans (split license fee over months)

  • F08.02.06 Shipped

    ✅ Payment reconciliation

  • F08.02.07 Planned

    ✅ Refund processing

  • F08.02.08 Planned

    ✅ Club bulk payment (club pays all member licenses)

  • F08.02.09 Planned

    ✅ Multi-currency support (international competitions)

  • F08.02.10 Shipped

    ✅ Payment audit trail

  • F08.02.11 Shipped

    ✅ Affiliate hybrid signup med Stripe Payment Link — gated av sys_support-godkännande, 14 d expiry, valuta klassificeras per land (SE→SEK, NO→NOK, DK→DKK, GB→GBP, CH→CHF, övriga→EUR), webhook driver provisionering via PL-T003

    ✅ PL-T302

Invoicing

F08.03
Platform+
How it works
  • F08.03.01 Shipped

    Invoice generation (line items, tax, totals)

    ✅ PL-F0803a
  • F08.03.02 Shipped

    Automatic invoice generation (license renewal, affiliation)

    ✅ PL-F0803a
  • F08.03.03 Shipped

    Invoice templates per federation

    ✅ PL-F0803a
  • F08.03.04 Shipped

    Invoice delivery (email, download)

    ✅ PL-F0803a
  • F08.03.05 Planned

    ✅ Credit note management

  • F08.03.06 Shipped

    ✅ Invoice status tracking (draft, sent, paid, overdue)

  • F08.03.07 Planned

    ✅ Payment reminder automation

  • F08.03.08 Shipped

    ✅ Debt collection workflow — involuntary dunning lifecycle: banner → 6 months read-only → +12 months data deletion

    ✅ PL-T047
  • F08.03.09 Shipped

    Multi-issuer invoicing — Invoice.issuer_type ∈ {platform, tenant, org_node} + issuer_tenant_id/issuer_org_node_id separerar utställare från gäldenär; cross-tenant-fakturor möjliga (FIPJP → nationsförbund); validatorer enforcerar konsistens; backfill-skript sätter historiska fakturor till platform-utställda.

    ✅ PL-T091
  • F08.03.10 Shipped

    Multi-language invoicing + ECB reference rate — Invoice.language (ISO-639-1) + per-språk PDF-templates (templates/invoice/{template_name}/{lang}.html) med en.html-fallback + structlog-warning; plattformsfakturor låsta till engelska; ECB-referenskurs fångad per faktura vid utfärdande (ecb_reference_rate + ecb_reference_date) för audit-spårbarhet i cross-currency-scenarier.

    ✅ PL-T091

Dunning & Payment Enforcement

F08.09
Planned

**Status:** PL-T047 implemented

How it works
  • F08.09.01 Shipped

    Involuntary dunning lifecycle: banner_only → final_warning → read_only → pre_deletion → deleted

    ✅ PL-T047
  • F08.09.02 Shipped

    Persistent payment-overdue banner visible to all tenant users (not just admins)

    ✅ PL-T047
  • F08.09.03 Shipped

    Email cascade to all tenant admins at each dunning stage transition

    ✅ PL-T047
  • F08.09.04 Shipped

    Stripe webhook integration: invoice.payment_failed opens case, invoice.paid closes (if ≤ final_warning)

    ✅ PL-T047
  • F08.09.05 Shipped

    Contract billing integration: 30-day overdue triggers dunning case

    ✅ PL-T047
  • F08.09.06 Shipped

    Platform-admin manual waive with mandatory reason + governance audit log

    ✅ PL-T047
  • F08.09.07 Shipped

    Anti-scope: no auto-reactivation after read_only — manual waive required

    ✅ PL-T047
  • F08.09.08 Shipped

    Voluntary cancellation during active dunning case closes dunning cleanly

    ✅ PL-T047

Federation Budgeting

F08.04
Planned
How it works
  • F08.04.01 Shipped

    Annual budget creation per federation

    ✅ PL-F0804
  • F08.04.02 Shipped

    Budget categories (competitions, administration, development, travel, etc.)

    ✅ PL-F0804
  • F08.04.03 Shipped

    Budget vs. actual tracking

    ✅ PL-F0804
  • F08.04.04 Shipped

    Budget approval workflow

    ✅ PL-F0804
  • F08.04.05 Shipped

    Financial report generation (annual, quarterly)

    ✅ PL-F0804
  • F08.04.06 Shipped

    Bookkeeping / ledger entries

    ✅ PL-F0804

Sponsorship & Grants

F08.05
Planned
How it works
  • F08.05.01 Shipped

    Sponsor registry (company, contact, contract period, amount)

    ✅ PL-F0805
  • F08.05.02 Shipped

    Sponsorship contract management

    ✅ PL-F0805
  • F08.05.03 Shipped

    Sponsor exposure tracking (logo placement, mentions)

    ✅ PL-F0805
  • F08.05.04 Shipped

    Grant application tracking (government, sports council)

    ✅ PL-F0805
  • F08.05.05 Shipped

    Grant reporting and accountability

    ✅ PL-F0805
  • F08.05.06 Shipped

    Sponsorship revenue allocation (competition, federation, club)

    ✅ PL-F0805
  • F08.05.07 Shipped

    Grant application workflow — MinistryGrantApplication med 8-state-machine (draft → submitted → under_review → awarded → in_progress → reporting_due → closed; rejected som terminal-gren)

    ✅ PL-T223
  • F08.05.08 Shipped

    Multi-stage status — capability-gated transitions via submit/record_decision/mark_in_progress/close med immutabel audit-trail

    ✅ PL-T223
  • F08.05.09 Shipped

    Reporting-due reminder — daglig cron-trigg 30/14/7 dagar före closes_at, deduplicerad per bucket, status promoveras till reporting_due vid minsta bucket

    ✅ PL-T223

Prize Money & Awards

F08.06
Planned
How it works
  • F08.06.01 Planned

    ✅ Prize pool configuration per competition

  • F08.06.02 Planned

    ✅ Prize money distribution rules

  • F08.06.03 Planned

    ✅ Winner payment processing

  • F08.06.04 Planned

    ✅ Tax reporting for prize money

  • F08.06.05 Planned

    ✅ Trophy and medal inventory tracking

Expense Management

F08.07
Planned
How it works
  • F08.07.01 Shipped

    Expense claim submission (umpires, officials, delegates)

    ✅ PL-F0807
  • F08.07.02 Shipped

    Travel expense calculation (distance, per diem)

    ✅ PL-F0807
  • F08.07.03 Shipped

    Expense approval workflow

    ✅ PL-F0807
  • F08.07.04 Shipped

    Expense reimbursement processing

    ✅ PL-F0807
  • F08.07.05 Shipped

    Expense category reporting

    ✅ PL-F0807

Financial Reporting

F08.08
Platform+
How it works
  • F08.08.01 Shipped

    Revenue reports (by source: licenses, competitions, sponsorship)

  • F08.08.02 Shipped

    Expense reports (by category)

  • F08.08.03 Planned

    Federation financial summary (upward reporting)

  • F08.08.04 Planned

    Club financial health indicators

  • F08.08.05 Planned

    Multi-year trend analysis

  • F08.08.06 Planned

    Export to accounting software (CSV, SAFT, SIE)

Bokföring och redovisning

F08.10
Platform+
How it works
  • F08.10.01 Shipped

    Sverige - Fortnox-export — semikolonseparerad CSV med VER/Serie/Vernr/Datum/Text/Konto/Debet/Kredit. Svenskt decimalformat (komma). Exporterar endast bokförda verifikationer. Använder per-tenant kontoplansmappning.

    ✅ PL-F0810a
  • F08.10.02 Shipped

    International - Xero-export — kommaseparerad CSV med *Date/*Description/*AccountCode/*Debit/*Credit/TaxType/Reference. Internationellt decimalformat (punkt). Per-tenant kontoplansmappning.

    ✅ PL-F0810a
  • F08.10.03 Shipped

    Sverige - Visma eEkonomi-export — tabbseparerad textfil med VER/Serie/Vernr/Datum/Text/Konto/Debet/Kredit/Projekt/Resultatenhet. Svenskt decimalformat. Stödjer API-synkronisering via integrationsconfig.

    ✅ PL-F0810a
  • F08.10.04 Shipped

    Per-tenant chart of accounts mapping — ChartOfAccountsMapping-samling tenant-scopad. Översätter interna BAS-konton till externa leverantörskonton. En aktiv mappning per provider per tenant. CRUD med duplikatskydd (409).

    ✅ PL-F0810a
  • F08.10.05 Shipped

    SIE4-export med IB/UB/RES — utökad SIE4-export (GET /accounting-export/sie4) med #KTYP kontotyper, #IB ingående balanser, #UB utgående balanser, #RES resultaträkningskonton. CP437-encoding, fiscal_year/company_name/org_number-parametrar.

    ✅ PL-F0810b
  • F08.10.06 Shipped

    Bokslut per säsong — generering av bokslut (financial statements) med resultaträkning- och balansräkningsögonblicksbilder. Status draft→finalized livscykel. Valfri periodlåsning vid finalisering. CRUD /financial-statements/.

    ✅ PL-F0810b
  • F08.10.07 Shipped

    Revisionsspår och låsning av perioder — periodlåsning förhindrar skapande/bokföring/makulering av verifikationer inom låst datumintervall. CRUD + check + unlock /period-locks/. Överlappande lås avvisas. Komplett audit trail via FinancialAuditLog.

    ✅ PL-F0810b
  • F08.10.08 Shipped

    Resultaträkning och balansräkning per OrgNode — konsoliderade rapporter per kostnadställe. GET /accounting-reports/consolidated-income-statement och /consolidated-balance-sheet med org_node_id-nedbrytning. Kvartalsfilter.

    ✅ PL-F0810b
  • F08.10.09 Shipped

    Bokio SIE4-export — Bokio-specifik SIE4-variant med UTF-8 BOM, CRLF-radslut, BAS-2024-validering (1000–8999), obligatoriskt #ORGNR. GET /accounting/bokio/export/sie4.

    ✅ PL-T078
  • F08.10.10 Shipped

    Bokio direkt-API — OAuth2-onboarding + synkron/batch-POST av verifikationer till Bokio REST API med idempotency-nycklar. Token-refresh 5 min före expiry. Error-hantering: 401 retry, 409 ignore, 422 fail, 429 backoff.

    ✅ PL-T078
  • F08.10.11 Shipped

    Bokio sync-jobb — batch-sync var 15 min (eller on-demand), plockar entries med synced_to_bokio_at is None, POST till Bokio per entry, markerar synced med external_refs.bokio. Max 3 retries med exponentiell backoff.

    ✅ PL-T078
  • F08.10.12 Shipped

    Bokio onboarding-flow — POST /accounting/bokio/configure (company_id, sync_mode api/sie4_only), OAuth-URL för API-läge, test-connection via get_fiscal_years(). Admin-vy med provider-kort, Sync Now-knapp, SIE4-nedladdning.

    ✅ PL-T078

Per-nationell ekonomi och avgifter

F08.09
Planned
How it works
  • F08.09.01 Shipped

    Per-tenant license fee with annual revision

    ✅ PL-F0809a
  • F08.09.02 Shipped

    Per-tenant VAT/MOMS/TVA/IVA configuration

    ✅ PL-F0809a
  • F08.09.03 Shipped

    Per-tenant invoice number series

    ✅ PL-F0809a
  • F08.09.04 Shipped

    Per-tenant payment term rules (30/60/90 days)

    ✅ PL-F0809a
  • F08.09.05 Shipped

    France SEPA Direct Debit for club fees

    ✅ PL-F0809a
  • F08.09.06 Shipped

    Sverige - Swish som default-betalsätt — per-debtor-type betalningspolicy med min/max-belopp, Swish som default för spelare

    ✅ PL-F0809b
  • F08.09.07 Shipped

    Deutschland - SEPA-Lastschrift — Basislastschrift (CORE) och Firmenlastschrift (B2B) via SEPA-mandathantering

    ✅ PL-F0809b
  • F08.09.08 Shipped

    Espana - Bizum för spelare-betalningar — Bizum med beloppstak (max 1 000 EUR), fallback till kort vid överskridande

    ✅ PL-F0809b
  • F08.09.09 Shipped

    Per-tenant valutahantering — basvaluta, stödda valutor, decimalplatser, avrundningsläge, visningsformat

    ✅ PL-F0809b
  • F08.09.10 Shipped

    Multi-currency rapportering för FIPJP/CEP — konsoliderade flervaualtarapporter med växelkursupplösning, kategoribrytning, finalisering

    ✅ PL-F0809b

Pricing Catalog

F08.10
Planned
How it works
  • F08.10.01 Shipped

    14-SKU pricing catalog data model — TenantBillingType (14 enums), BillingMode (self_service/contract), PricingCatalogVersion (versioned, immutable), PricingTier (per-version, per-billing-type, Decimal EUR price, i18n display/description/includes).

    ✅ PL-T011
  • F08.10.02 Shipped

    Seed data v2026.01 — Idempotent seed script skapar initial katalog med exakta priser från market-analysis.md A2. 7 self-service + 7 contract tiers. i18n för en/fr/es/sv.

    ✅ PL-T011
  • F08.10.03 Shipped

    Admin pricing catalog API — GET /admin/pricing-catalog/current (14 tiers), /versions (historik), /versions/{id}, /tiers/{billing_type}. Platform-admin auth.

    ✅ PL-T011
  • F08.10.04 Shipped

    Public pricing API — GET /public/pricing (7 self-service tiers, no auth, Cache-Control max-age=300).

    ✅ PL-T011
  • F08.10.05 Shipped

    Admin pricing catalog UI — Read-only list- och detaljvy i admin under Platform > Pricing Catalog. 14 tiers i tabell, detaljvy med i18n-fält, Stripe-ID:n, license-range.

    ✅ PL-T011
  • F08.10.06 Shipped

    National tier auto-classification — resolve_national_tier_from_license_count: 0–999→XS, 1000–2999→S, 3000–9999→M, ≥10000→L.

    ✅ PL-T011
  • F08.10.07 Shipped

    Publicera ny version (write-UI i admin)

    ✅ PL-F0810b
  • F08.10.08 Shipped

    Prishistorik per kund (koppling via Subscription)

    ✅ PL-F0810b
  • F08.10.09 Shipped

    Tenant-typ-bryggad pricing-katalog (PL-T226) — PricingTier utökas med tenant_type (broad till TenantType) + denormaliserade domain_count / subsystem_count / feature_count från TENANT_TIER_MATRIX. TENANT_TYPE_FOR_BILLING_TYPE ger reverse-lookup för Stripe-webhook + drift-job. Migration tools/migrations/pl-t226-tenant-type-pricing.py är idempotent.

    ✅ PL-T226
  • F08.10.10 Shipped

    Public detail-endpoint för tier-jämförelser (PL-T226) — GET /public/pricing-tiers (full 14-rad-katalog med ?include_legacy=true för 15) + GET /public/pricing-tiers/{tenant_type} (deep-link med included_subsystems / excluded_subsystems från matrisen). 5 min cache. Service tier_for_tenant_type() routar FEDERATION mot national_* via license-count.

    ✅ PL-T226
  • F08.10.11 Shipped

    Pricing tier sync-check job (PL-T226) — nattlig jämförelse av PricingTier-tabellen mot www/src/data/tenant-tiers.ts. SEV2 platform-event vid divergens.

    ✅ PL-T226

Subscription Management

F08.11
Planned

## Implementation Status

How it works
  • F08.11.01 Shipped

    Subscription datamodell med pricing_version — varje Subscription kopplas till en pricing_version (ISO-datum) som identifierar den prislista som gällde vid avtalstillfället. Oförändrat vid förnyelse. Indexerat för rapportqueries.

    ✅ PL-T042
  • F08.11.02 Shipped

    Price-Lock Guarantee — formell policy: priset som pricing_version anger gäller så länge Subscription:en är aktiv. Inga automatiska prishöjningar vid förnyelse. Publicerad på petanque.life/price-lock-guarantee (4 språk).

    ✅ PL-T042
  • F08.11.03 Shipped

    Subscription CRUD-livscykel (PL-T285) — PUT /subscriptions/{id} uppgraderar tier inom låst pricing_version (downgrades blockas 422). POST /subscriptions/{id}/cancel med immediate=False (default) eller True (sys_finance + fresh-auth). POST /subscriptions/{id}/renew är idempotent på (subscription_id, period_start, period_end). DELETE /subscriptions/{id} soft-deletar (sys_finance + fresh-auth) — blockeras vid obetalda fakturor. Audit via write_finance_audit.

    ✅ PL-T285
  • F08.11.04 Shipped

    Pricing-version-rapport (PL-T285) — GET /sys/billing/subscription-report/by-pricing-version?include_cancelled=<bool> aggregerar antal aktiva subscriptions och låst årsintäkt per pricing_version, plus tenant-typ-fördelning. Cache 5 min via Redis. Sys-konsol-vy sys/app/(dashboard)/billing/subscription-report.tsx med trendgraf och tabell.

    ✅ PL-T285
  • F08.11.05 Shipped

    Auto-renewal-jobb (PL-T285) — subscription_auto_renewal_job körs varje timme via craft-easy-jobs, plockar auto_renew=True AND status=ACTIVE AND next_renewal_at <= now, skapar Invoice via existerande pipeline med oförändrat pricing_version. Retry 1h/2h/4h, sedan dunning (PL-T047). Drift-detektion subscription_renewal_drift (SEV2) när jobbet inte körts >25h. Audit subscription.auto_renewed / subscription.auto_renewal_failed.

    ✅ PL-T285
  • F08.11.06 Shipped

    ECB-kvartalsfixing för lokal valuta — formell policy: fakturering i lokal valuta sker enligt ECB:s kvartalsfixade referensväxelkurser. Fixing första bankdagen i januari, april, juli, oktober. Stödda valutor: EUR, SEK, NOK, DKK, GBP, USD, CHF. Varje faktura sparar ecb_fixing_rate och ecb_fixing_date för historisk reproducerbarhet. Publicerad på petanque.life/currency-policy (4 språk).

    ✅ PL-T043
  • F08.11.07 Shipped

    Tenant-typ-tier-byte via Stripe Subscription Schedule (PL-T226) — POST /sys/billing/{tenant_id}/change-tier (sys_finance + fresh-auth) byter TenantConfig.tenant_type, repointar TenantSubscription mot ny TenantBillingType, skapar Stripe Subscription Schedule prorated, invaliderar TenantTierResolver-cachen och skriver tenant.tier.billing_changed + tenant.tier.synced_from_stripe-audit. Companion-endpoint …/change-tier/quote är read-only förhandsbesked. Downgrades från federation-grade SKU blockas (422 tier_downgrade_blocked).

    ✅ PL-T226
  • F08.11.08 Shipped

    Tenant-facing upgrade-quote + ROI-räknare (PL-T226) — POST /admin/billing/upgrade-quote returnerar prorated belopp + pre-fylld contact-sales/?topic=tier-upgrade-URL. Admin-vyn views/billing/SubscriptionView.tsx läser GET /me/tenant-tier, listar inkluderade delsystem och låter kunden simulera tier-byte. Faktiska byten ligger hos sys-operator.

    ✅ PL-T226
  • F08.11.09 Shipped

    Stripe webhook tenant_type-sync + drift-job (PL-T226) — customer.subscription.updated läser metadata.tenant_type, applicerar om det driftat, skriver tenant.tier.synced_from_stripe-audit + bustar tier-resolver-cachen. Job stripe_subscription_drift_check (varje timme) fångar oöverensstämmelser → billing_health=warning + SEV2 platform-event.

    ✅ PL-T226
  • F08.01.01 Shipped

    /license-fee-configs/, /license-fee-overrides/, /license-fees/calculate

    ✅ PL-204
  • F08.01.02 Shipped

    /fee-schedules/ (fee_category=club_affiliation, med affiliation_tiers)

    ✅ PL-801
  • F08.01.03 Shipped

    /fee-schedules/ (fee_category=competition_entry)

    ✅ PL-801
  • F08.01.04 Shipped

    /fee-management/copy-season (säsongskopiering), /fee-management/by-season/{id} (konsoliderad vy)

    ✅ PL-F0801a
  • F08.01.05 Shipped

    /fee-surcharge-rules/ (CRUD), /fee-surcharges/calculate (early bird/late fee)

    ✅ PL-F0801a
  • F08.01.07 Shipped

    /revenue-distribution-rules/ + automatisk distribution vid betalning

    ✅ PL-801
  • F08.01.09 Shipped

    /fee-schedules/ (fee_category=sanction)

    ✅ PL-801
  • F08.02.01 Shipped

    /payments/ (registrera, spåra, återbetala)

    ✅ PL-801
  • F08.03.01 Shipped

    /invoices/ (skapa, rader, summor)

    ✅ PL-801
  • F08.03.02 Shipped

    Automatisk faktura vid licensgodkännande

    ✅ PL-801
  • F08.03.06 Shipped

    Fakturastatus: draft → sent → paid/overdue/cancelled

    ✅ PL-801
  • F08.03.07 Shipped

    /invoices/{id}/send-reminder

    ✅ PL-801
  • F08.03.08 Shipped

    /debts/check/{debtor_id} — blockerar transfer och licensförnyelse

    ✅ PL-801
  • F08.08.01 Shipped

    /financial-reports/revenue, /financial-reports/revenue/by-org-node

    ✅ PL-801
  • F08.02.02 Shipped

    /checkout/ (Stripe Checkout Session), /webhooks/stripe

    ✅ PL-802
  • F08.02.02 Shipped

    /checkout/ (Swish QR-betalning), /webhooks/swish

    ✅ PL-802
  • F08.02.04 Shipped

    /receipts/, /receipts/{id}/pdf (kvittogenerering)

    ✅ PL-802
  • F08.02.06 Shipped

    Automatisk avstämning via Stripe/Swish webhooks

    ✅ PL-802
  • F08.02.07 Shipped

    /payments/{id}/gateway-refund (återbetalning via gateway)

    ✅ PL-802
  • F08.02.10 Shipped

    Betalningsspårning: /payment-history/{debtor_id}

    ✅ PL-802
  • F08.03.01 Shipped

    /invoices/ utökad med tax_rate per rad, tax_amount, tax_total

    ✅ PL-F0803a
  • F08.03.02 Shipped

    /invoices/auto-generate/license-renewal, /invoices/auto-generate/affiliation

    ✅ PL-F0803a
  • F08.03.03 Shipped

    /invoice-templates/ (CRUD, per-federation, default-hantering)

    ✅ PL-F0803a
  • F08.03.04 Shipped

    /invoices/{id}/pdf (PDF-nedladdning), /invoices/{id}/deliver (e-postleverans)

    ✅ PL-F0803a
  • F08.03.05 Shipped

    /credit-notes/ (CRUD, issue, apply, void — kreditnotor mot fakturor)

    ✅ PL-F0803b
  • F08.03.06 Shipped

    /invoices/{id}/status-history, /invoices/mark-overdue (statusspårning med historik)

    ✅ PL-F0803b
  • F08.03.07 Shipped

    /reminder-configs/, /invoices/reminder-queue, /invoices/send-due-reminders (påminnelseautomatik)

    ✅ PL-F0803b
  • F08.03.08 Shipped

    /debt-collection/ (CRUD, escalate, resolve — inkassoworkflow)

    ✅ PL-F0803b
  • F08.05.01 Shipped

    /sponsors/ (CRUD med kontaktinfo, nivå, sponsrade entiteter)

    ✅ PL-F0805
  • F08.05.02 Shipped

    /sponsors/{id}/contracts (kontraktshantering med belopp, period, villkor)

    ✅ PL-F0805
  • F08.05.03 Shipped

    /sponsors/{id}/exposure (exponeringsregistrering och sammanfattning)

    ✅ PL-F0805
  • F08.05.04 Shipped

    /grants/ (ansökan: draft → submitted → approved/rejected → reporting)

    ✅ PL-F0805
  • F08.05.05 Shipped

    /grants/{id}/reports (bidragsrapportering efter godkännande)

    ✅ PL-F0805
  • F08.05.06 Shipped

    /sponsor-allocations/ (intäktsallokering per OrgNode med procentsatser)

    ✅ PL-F0805
  • F08.04.01 Shipped

    /budgets/ (CRUD med inkomst-/utgiftskategorier per OrgNode), /budgets/copy (kopiera med inflationsjustering)

    ✅ PL-F0804
  • F08.04.02 Shipped

    BudgetCategory med type: income/expense, kopplad till kontoplan

    ✅ PL-F0804
  • F08.04.03 Shipped

    /budgets/{id}/actualize + /budgets/{id}/variance (budget vs. faktisk)

    ✅ PL-F0804
  • F08.04.04 Shipped

    draft → proposed → approved → closed (godkännandeflöde med audit log)

    ✅ PL-F0804
  • F08.04.05 Shipped

    /accounting-reports/income-statement (årsvis + kvartalsvis med quarter-param), /accounting-reports/budget-tracking, /accounting-reports/balance-sheet

    ✅ PL-F0804
  • F08.04.06 Shipped

    /accounting-entries/ (verifikationer med kontoplan), /accounting-export/sie + /fec + /json

    ✅ PL-F0804
  • F08.01.04 Shipped

    /fee-management/copy-season (kopiera avgiftsdata), /fee-management/by-season/{id}

    ✅ PL-F0801a
  • F08.01.05 Shipped

    /fee-surcharge-rules/ (CRUD), /fee-surcharges/calculate (beräkning)

    ✅ PL-F0801a
  • F08.01.06 Shipped

    /fee-waiver-rules/ (CRUD), /fee-waivers/calculate (beräkning med ålder/familj/scoping)

    ✅ PL-F0801b
  • F08.01.07 Shipped

    /fee-splitting/calculate (fördelningsberäkning), /fee-splitting/execute (skapar RevenueEntry)

    ✅ PL-F0801b
  • F08.01.08 Shipped

    /fee-audit/ (lista), /fee-audit/{id} (detalj), /fee-audit/entity/{type}/{id} (per entitet)

    ✅ PL-F0801b
  • F08.01.09 Shipped

    /sanction-fee-configs/ (CRUD), /sanction-fee-configs/calculate/{season_id} (beräkning)

    ✅ PL-F0801b
  • F08.01.10 Shipped

    /suspension-fee-configs/ (CRUD), /suspension-fee-configs/calculate/{season_id} (beräkning)

    ✅ PL-F0801b
  • F08.01.06 Shipped

    /fee-waiver-rules/ (CRUD), /fee-waivers/calculate

    ✅ PL-F0801b
  • F08.01.07 Shipped

    /fee-splitting/calculate, /fee-splitting/execute

    ✅ PL-F0801b
  • F08.01.08 Shipped

    /fee-audit/, /fee-audit/{id}, /fee-audit/entity/{type}/{id}

    ✅ PL-F0801b
  • F08.01.09 Shipped

    /sanction-fee-configs/ (CRUD), /sanction-fee-configs/calculate/{season_id}

    ✅ PL-F0801b
  • F08.01.10 Shipped

    /suspension-fee-configs/ (CRUD), /suspension-fee-configs/calculate/{season_id}

    ✅ PL-F0801b
  • F08.07.01 Shipped

    /expense-claims/ (CRUD med specificerade utgiftsposter, kvitto-URL, käll-referens)

    ✅ PL-F0807
  • F08.07.02 Shipped

    /expenses/travel/calculate (km-ersättning + traktamente), embedded TravelExpense i claims

    ✅ PL-F0807
  • F08.07.03 Shipped

    /expense-claims/{id}/submit, /expense-claims/{id}/approve, /expense-claims/{id}/reject

    ✅ PL-F0807
  • F08.07.04 Shipped

    /expense-claims/{id}/pay (utbetalning med valfri betalningsmetod och referens)

    ✅ PL-F0807
  • F08.07.05 Shipped

    /expense-reports/by-category, /expense-reports/by-org-node (rapporter med filter)

    ✅ PL-F0807
  • F08.06.01 Shipped

    /prize-pools/ (CRUD, per tävling, med sponsor-koppling)

    ✅ PL-F0806
  • F08.06.02 Shipped

    /prize-pools/{id}/recalculate-distribution (procentbaserad omfördelning), /prize-pools/{id}/generate-payouts (auto från resultat)

    ✅ PL-F0806
  • F08.06.03 Shipped

    /prize-payouts/{id}/approve (godkänn med skatteberäkning), /prize-payouts/{id}/process-payment (koppla betalning), /prize-payouts/batch-approve (batchgodkännande)

    ✅ PL-F0806
  • F08.06.04 Shipped

    /prize-payouts/calculate-tax (förhandsberäkning), /prize-payouts/tax-report (aggregerad skatterapport per land)

    ✅ PL-F0806
  • F08.06.05 Shipped

    /trophy-inventories/ (CRUD), /trophy-awards/ (CRUD), /prize-pools/{id}/generate-awards (auto från resultat)

    ✅ PL-F0806
  • F08.09.01 Shipped

    /license-fee-revisions/ (CRUD, approve, apply — årlig revision av licensavgifter)

    ✅ PL-F0809a
  • F08.09.02 Shipped

    /tenant-vat-configs/ (CRUD), /tenant-vat-configs/active, /tenant-vat-configs/resolve-rate

    ✅ PL-F0809a
  • F08.09.03 Shipped

    /invoice-number-series/ (CRUD), /invoice-number-series/preview

    ✅ PL-F0809a
  • F08.09.04 Shipped

    /payment-term-rules/ (CRUD), /payment-term-rules/resolve

    ✅ PL-F0809a
  • F08.09.05 Shipped

    /sepa-mandates/ (CRUD, activate, cancel), /sepa-direct-debits/ (CRUD, submit, settle, reject)

    ✅ PL-F0809a
  • F08.08.01 Shipped

    /financial-reporting/revenue-summary (intäkter per källa: licenser, tävlingar, sponsring, bidrag)

    ✅ PL-F0808
  • F08.08.02 Shipped

    /financial-reporting/expense-summary (kostnader per kategori: utlägg + bokföringsposter)

    ✅ PL-F0808
  • F08.08.03 Shipped

    /financial-reporting/federation-summary (hierarkisk sammanställning per OrgNode-nivå + budget)

    ✅ PL-F0808
  • F08.08.04 Shipped

    /financial-reporting/club-health (hälsoindikatorer: betalningsgrad, kostnadskvot, förfallna fakturor)

    ✅ PL-F0808
  • F08.08.05 Shipped

    /financial-reporting/trend-analysis (flerårsanalys med tillväxttakt, max 10 år)

    ✅ PL-F0808
  • F08.08.06 Shipped

    /financial-reporting/export/csv (UTF-8 BOM), /financial-reporting/export/saft (SAF-T XML, OECD)

    ✅ PL-F0808
  • F08.10.01 Shipped

    GET /accounting-export/fortnox (semikolonseparerad CSV, svenskt decimalformat, per-tenant kontoplansmappning)

    ✅ PL-F0810a
  • F08.10.02 Shipped

    GET /accounting-export/xero (kommaseparerad CSV, internationellt decimalformat, per-tenant kontoplansmappning)

    ✅ PL-F0810a
  • F08.10.03 Shipped

    GET /accounting-export/visma (tabbseparerad textfil, svenskt decimalformat, API-synk via integrationsconfig)

    ✅ PL-F0810a
  • F08.10.04 Shipped

    /chart-of-accounts-mappings/ (CRUD, per provider per tenant, duplikatskydd 409)

    ✅ PL-F0810a
  • F08.10.05 Shipped

    GET /accounting-export/sie4 (utökad SIE4 med #KTYP, #IB, #UB, #RES, CP437)

    ✅ PL-F0810b
  • F08.10.06 Shipped

    /financial-statements/ (CRUD, generering bokslut med resultat-/balansögonblicksbild, finalisering)

    ✅ PL-F0810b
  • F08.10.07 Shipped

    /period-locks/ (CRUD, check, unlock, blockerar entry create/post/void inom låst period)

    ✅ PL-F0810b
  • F08.10.08 Shipped

    /accounting-reports/consolidated-income-statement, /consolidated-balance-sheet (per OrgNode, kvartal)

    ✅ PL-F0810b

Bank Reconciliation

F08.12
Planned

_PL-T076 — import av kontoutdrag, regelbaserad auto-matchning, manuell inbox, AccountingEntry-generering, lock-flöde._

How it works
  • F08.12.01 Shipped

    Import av kontoutdrag — stöd för CAMT.053 (ISO 20022), CSV (generisk + bankspecifik: Nordea, SEB, Handelsbanken, Swedbank, BNP Paribas, Crédit Agricole), OFX, SIE4. Formatdetektering automatisk. SHA-256-deduplicering.

    ✅ PL-T076
  • F08.12.02 Shipped

    Regelbaserad auto-matchning — klubbkassörens egna regler (motpartsnamn-regex, beloppsintervall, OCR-prefix). Prioritetsordning. Stöder book_to_account, match_to_invoice_by_ocr och ignore.

    ✅ PL-T076
  • F08.12.03 Shipped

    Heuristisk matchning — referens/OCR-matchning mot Payment (1.0 conf), fuzzy namn+belopp+datum ±3d (Jaro-Winkler ≥ 0.85). Under 0.6 → unmatched.

    ✅ PL-T076
  • F08.12.04 Shipped

    Manuell inbox — obematchade rader med färgkodning (grön/gul/röd). Tre-knapps-flöde: matcha mot faktura, bokför som ny post, ignorera. Split-matchning stöds.

    ✅ PL-T076
  • F08.12.05 Shipped

    AccountingEntry-generering — automatisk dubbelbokning vid matchning: debet bank/kredit kundfordran (incoming), debet leverantörsskuld/kredit bank (outgoing), debet bank/kredit intäktskonto (new).

    ✅ PL-T076
  • F08.12.06 Shipped

    Saldo-avstämning och lock — closing_balance == opening_balance + sum(lines) OCH alla lines matchade ⇒ status=locked. Balance-check-endpoint returnerar expected/actual/diff.

    ✅ PL-T076

Tenant Billing Profile

F08.13
Planned

_PL-T092 — tenant-scoped singleton som styr språk, betalningstid, påminnelsekadens, ton, dröjsmålspolicy och utställar-identitet._

How it works
  • F08.13.01 Shipped

    BillingProfile-modell — tenant-scoped singleton med språk/valuta/betalningstid/ton/dunning-stages/dröjsmålsavgift/issuer-identitet. Fyra pydantic-validatorer (språk, dröjsmåls-policy→fält, dunning-kadens sortering/unika, payment-terms-ton).

    ✅ PL-T092
  • F08.13.02 Shipped

    Konfigurerbar dunning-kadens — DunningStageConfig med offset_days (0–720), stage_name, template_id, channel (email/letter/both). Stigande sortering + unika offsets/namn enforced.

    ✅ PL-T092
  • F08.13.03 Shipped

    Dröjsmålsavgifts-policy — none/fixed/percentage/interest_rate med matchande fält-kravsvalidering och legal_reference för nationell lagstiftning (Code de commerce L. 441-6, BGB § 288 Abs. 2, Inkassolagen 1974:182 § 6).

    ✅ PL-T092
  • F08.13.04 Shipped

    Profil-cache — in-process TTL 60 s per tenant; invalidate_billing_profile_cache(tenant_id) vid alla mutationer. ≤ 50 ms p95 på cold read, ≪ 1 ms på cache hit.

    ✅ PL-T092
  • F08.13.05 Shipped

    Platform defaults fallback — get_platform_defaults() returnerar hårdkodad konfig (Easy Software System In Europe AB, engelska, EUR, 30d, 14/30/60 dunning). resolve_profile_or_defaults fallback-loggar strukturerad varning (billing_profile.fallback_to_defaults).

    ✅ PL-T092
  • F08.13.06 Shipped

    API — GET/PUT /tenants/{id}/billing-profile (tenant-admin RBAC via _assert_tenant_access), POST /tenants/{id}/billing-profile/dunning-stages/preview för dry-run av kadensändringar (antal fakturor + total_outstanding per stadium idag).

    ✅ PL-T092
  • F08.13.07 Shipped

    Onboarding-seed — create_default_billing_profile(tenant_id, issuer_*, ...) skapar profilen vid tenant-onboarding; avvisar dubbel-skapelse på service-nivå (singleton-invariant ägd av servicen, ej DB-constraint).

    ✅ PL-T092

Pro Services Billing

F08.11
Planned

_PL-T036 — timbaserad fakturering för konsultengagemang (€150/h)._

How it works
  • F08.11.01 Shipped

    ProServiceEngagement — scoped consulting assignment per tenant med låst timpris (€150/h), estimerat timantal och faktureringskontakt

    ✅ PL-T036
  • F08.11.02 Shipped

    TimeEntry med kvartstimme-precision — DRAFT→SUBMITTED→INVOICED/VOIDED livscykel; max 24h per konsult per dag

    ✅ PL-T036
  • F08.11.03 Shipped

    Månadsvis faktureringsjobb — idempotent jobb skapar ProServiceInvoice per tenant 1:a varje månad; markerar TimeEntries INVOICED

    ✅ PL-T036
  • F08.11.04 Shipped

    Finansiell separation — revenue_category = "pro_services_consulting" på alla dokument; aldrig inkluderad i subscription ARR

    ✅ PL-T036
  • F08.11.05 Shipped

    Kund-rapport — kunden ser egna SUBMITTED/INVOICED poster; consultant_user_id dold; DRAFT aldrig exponerat

    ✅ PL-T036
  • F08.11.06 Shipped

    Faktura-förhandsgranskning — admin kan se nästa månads fakturor innan jobbet körs

    ✅ PL-T036
  • F08.11.07 Shipped

    Pro Services revenue-rapport — månadsvis/årsvis rapport separerad från subscription ARR

    ✅ PL-T036

Bot Protection

F08.14
Planned

_PL-T072 — Cloudflare Turnstile + honeypot + rate-limit + audit på alla publika POST-endpoints._

How it works
  • F08.14.01 Shipped

    Turnstile-widget — Cloudflare Turnstile på alla publika POST-endpoints (auth, contract-requests, newsletter, contact, feedback, klubbansökan); osynlig 95 % av tiden; GDPR-vänlig (ingen cookie); fail-open med 5 s timeout.

    ✅ PL-T072
  • F08.14.02 Shipped

    Honeypot-fält — dolda formulärfält som botar fyller i; serverside-validering avvisar 422 om satta. Kompletterar Turnstile för enklaste botar.

    ✅ PL-T072
  • F08.14.03 Shipped

    Rate-limit-stack — IP-baserad rate-limit per endpoint (Redis-counter); 429 efter tröskel; dynamiska gränser per endpoint-känslighet.

    ✅ PL-T072
  • F08.14.04 Shipped

    Audit + alert — strukturerad logg på Turnstile-fail/honeypot-trigger/rate-limit-block; Prometheus-counter; alert vid spike (>10× baseline).

    ✅ PL-T072

Swedbank Bankgirot BGI-inläsning

F08.15
In progress

_PL-T095 — SFTP-dragen auto-matchning av inkommande Bankgirot-betalningar mot `Invoice.ocr_reference`. Kärnpipelinen (Luhn + parser + matchare + admin-kö + job) är klar; riktig SFTP-hämtning och Azure Key Vault-credentials landar i en uppföljning._

How it works
  • F08.15.01 Shipped

    Luhn mod-10-hjälpare — verify_luhn/generate_luhn_checksum/build_ocr_reference med testvektorer (Wikipedia, MasterCard). Strippar icke-siffror, zero-pad till ≥3, trim till ≤24, sist checksum.

    ✅ PL-T095
  • F08.15.02 Shipped

    BGMax-parser (legacy fixed-length) — record 01 (valuta pos 25–27), 05 (OCR pos 3–27, belopp pos 28–45 som öre, datum pos 46–53 YYYYMMDD, motpart pos 54–80). Okända record-typer hoppas över utan krasch.

    ✅ PL-T095
  • F08.15.03 Shipped

    ISO 20022 pain.002-parser — namespace-strippad iteration av TxInfAndSts-noder; stödjer pain.002.001.10–.12.

    ✅ PL-T095
  • F08.15.04 Shipped

    Auto-detect parser — auto_detect_and_parse väljer XML vs legacy på första non-whitespace-bytet.

    ✅ PL-T095
  • F08.15.05 Shipped

    Invoice.ocr_reference — nytt fält (4–25 siffror) + Pydantic-validator som auto-genererar från invoice_number om det saknas; index på (tenant_id, ocr_reference).

    ✅ PL-T095
  • F08.15.06 Shipped

    BankImportRun-modell — platform-scoped audit per fil med SHA-256 source_hash för idempotens, filename, fetched_at, format, rows-räknare, status (success/partial/failed).

    ✅ PL-T095
  • F08.15.07 Shipped

    UnmatchedBankTransfer-modell — platform-scoped kö med reason (no_ocr/invalid_ocr_checksum/no_matching_invoice/amount_overpaid) och resolution-fält (matched_invoice_id, ignored).

    ✅ PL-T095
  • F08.15.08 Shipped

    Matcher — cross-tenant lookup mot aktiva fakturor (sent/overdue/partially_paid), skapar Payment (method bank_transfer, status completed) + BankTransferMatch (status matched, confidence exact), uppdaterar invoice.paid_amount och status.

    ✅ PL-T095
  • F08.15.09 Shipped

    Övermatchning — när beloppet överstiger utestående skapas både payment och amount_overpaid unmatched-rad så admin kan hantera återbetalning.

    ✅ PL-T095
  • F08.15.10 Shipped

    SHA-256-idempotens — process_file_content returnerar befintlig BankImportRun vid återleverans; inga dubblerade betalningar.

    ✅ PL-T095
  • F08.15.11 Shipped

    Craft-easy-jobregistrering — bank-reconciliation-import med injicerbar fetcher: () -> Iterable[(filename, content)]; default-fetcher returnerar []. Tester byter fetcher på modulnivå.

    ✅ PL-T095
  • F08.15.12 Shipped

    Admin-kö — GET /admin/bank/unmatched, POST /admin/bank/unmatched/{id}/match?invoice_id=, POST /admin/bank/unmatched/{id}/ignore, GET /admin/bank/import-runs. Alla platform_admin-skyddade.

    ✅ PL-T095
  • F08.15.13 Shipped

    SFTP-hämtare — asyncssh-baserad fetcher mot Swedbanks SFTP-inbox med credentials från Azure Key Vault.

    ✅ PL-T095
  • F08.15.14 Shipped

    Move-on-processed — flytta fil till processed/ resp. failed/ efter varje försök så idempotens-hashet bara försvarar mot genuina dubletter.

    ✅ PL-T095
  • F08.15.15 Shipped

    Tenant-admin-kö — låt federationer hantera sina egna unmatched-rader (nuvarande surface är platform_admin-only eftersom inboxen är platform-vid).

    ✅ PL-T095
  • F08.15.16 Shipped

    Parse-failure-alert — Slack/email-notifiering när en BGI-fil misslyckas parsa.

    ✅ PL-T095

Tenant-admin UI för fakturering

F08.16
Planned

_PL-T096 — sex vyer i admin-appen under nytt sidomenysektion "Fakturering": tenant-scopade (fakturaprofil, fakturor, fakturadetalj, betalningar) och platform-admin-only (omatchade transfers, batch-jobb)._

How it works
  • F08.16.01 Shipped

    Central API-klient — admin/src/lib/billing-api.ts centraliserar alla anrop mot /tenants/{id}/billing-profile, /invoices, /payments, /admin/bank/* och craft-easy-/jobs/*; exponerar typer (BillingProfile, Invoice, Payment, UnmatchedTransfer, JobRun), hjälpfunktioner (formatMoney, invoiceStatusLabel, INVOICE_STATUSES), multi-status fan-out för listfiltret, och ApiError-genomsläpp.

    ✅ PL-T096
  • F08.16.02 Shipped

    BillingProfileView — editerar tenantens singleton-profil via GET/PUT /tenants/{id}/billing-profile. Dunning-stegar som editerbar tabell (add/remove/reorder, auto-sortering på save), språk som chip multi-select (default låst i supported-listan), villkorliga dröjsmålsfält per policy, POST .../dunning-stages/preview för dry-run. Client-side validering speglar backend-pydantic.

    ✅ PL-T096
  • F08.16.03 Shipped

    InvoiceList — GET /invoices/?status=... med multi-status-chip-filter (parallel fan-out + de-dup per id när flera valts), "Ladda fler 50"-paginering, sorterbara kolumner (Nummer, Debitor, Utfärdad, Förfaller, Summa, Status), statusfärgade pill-badges.

    ✅ PL-T096
  • F08.16.04 Shipped

    InvoiceDetail — full faktura-detalj: header med status-badge, info-kort (issuer, debitor, ECB-referenskurs, OCR), rader, betalningslista (parallell GET /payments/?invoice_id=), status_history-tidslinje, villkorliga actions (PATCH /send, POST /send-reminder, PATCH /cancel), PDF-länk via Linking.openURL.

    ✅ PL-T096
  • F08.16.05 Shipped

    PaymentList — GET /payments/?status= med radiofilter (alla/avslutade/pågående/misslyckade/återbetalda/avbrutna), paginering, row-click öppnar faktura. Kolumner: Betald, Belopp, Metod, Referens, Faktura (last-8), Status.

    ✅ PL-T096
  • F08.16.06 Shipped

    UnmatchedTransfersView (platform-admin) — GET /admin/bank/unmatched, include-resolved-toggle, row-actions via modal (Matcha via faktura-ID + POST .../match?invoice_id=...; Ignorera med motivering + POST .../ignore). Status-badges OLÖST/MATCHAD/IGNORERAD.

    ✅ PL-T096
  • F08.16.07 Shipped

    JobsAdmin (platform-admin) — kort-grid med alla /jobs/registry-jobb (Historik-filter + Kör nu), body-table med senaste 100 /jobs/runs-poster (manuellt-triggad-markör, tid i sekunder, status-färgad badge, felmeddelande 2 rader). POST /jobs/run/{name} med tom parameter-body.

    ✅ PL-T096
  • F08.16.08 Shipped

    Navigation — ny sidomenysektion "Fakturering" mellan Ekonomi och Innehåll med 5 länkar (3 tenant-scopade, 2 platform-admin-gatade via nytt requiresPlatformAdmin-flag i NavItem). Filtret appliceras både i visibleSectionsFor() och i ⌘K command palette.

    ✅ PL-T096
  • F08.16.09 Shipped

    requirePlatformAdmin-HOC — admin/src/auth/platform-admin.tsx: hämtar GET /session/me, tillåter scope === "platform_admin", role === "platform_admin" eller is_system_user, renderar annars 403-panel. Används av /billing/unmatched-transfers och /platform/jobs. Bara kosmetisk — backend enforceras oberoende.

    ✅ PL-T096
  • F08.16.10 Shipped

    usePlatformAdmin-hook — hook-variant för icke-blockerande villkorlig UI (sidomenyns dolda länkar, command palette-index). Läser samma /session/me-resultat.

    ✅ PL-T096

Multi-currency Foundation (PL-T224)

F08.17
Planned

`MoneyAmount` value object + manual `FXRate`-table + `FXService.convert`. Hedging hooks och real-time FX-feeds är **explicit out-of-scope** — se docs/business/finance/multi-currency-foundation.md.

  • F08.17.01 Shipped

    MoneyAmount value object — Pydantic-modell med currency: ISO4217 + amount: Decimal. Wire-format: amount serialiserad som string för float-säker JS-transport. Negativa belopp tillåts (refunds). Likhet currency-aware. Konsumeras av PracticeExam.fee och MentorAssignment.hourly_rate.

    ✅ PL-T224
  • F08.17.02 Shipped

    FXRate manual entry + lookup — Beanie-document med (base_currency, quote_currency, rate, as_of, source, is_active). POST /finance/fx/rates (capability finance.fx.manage) skriver manuella rates; GET /finance/fx/rates?base=&quote=&as_of= hämtar senaste matchande. Source-värdena ecb-daily och stripe-fx reserverade för framtida automation.

    ✅ PL-T224
  • F08.17.03 Shipped

    FXService.convert — POST /finance/fx/convert med {amount, currency, target_currency, as_of?}. base==quote returnerar input oförändrat utan DB-hit. Saknad rate ger 422 fx_rate_unavailable. Conversion runda till 0.01 ROUND_HALF_EVEN. Returnerar rate_used för audit-spårning.

    ✅ PL-T224