Aller au contenu

TCR Pipeline — FATCA Chapter 4 + CRS OCDE

Modules : tcr-svc (orchestrateur Temporal), indicia-collector, classifier-engine, balance-aggregator, xml-generator, submit-adapter.

ADRs : ADR-001 (Temporal), ADR-005 (MinIO WORM), ADR-007 (capture indicia), ADR-025 (DSL réutilisé), ADR-026 (CPS), ADR-034 (architecture TCR).

POCs : poc-fatca-generator (XML FATCA v2.0 DGI TN/IRS), poc-crs-generator (XML CRS v2.0 OCDE).

Standards : IRS Pub 5124 (FATCA XML v2.0), OECD CRS XML User Guide v2.0, IGA Tunisie-USA modèle 1 (2014), Loi 2016-71 (Tunisie CRS).

Ce document fixe la spec d’ingénierie complète de la pipeline TCR (Tax Compliance & Reporting). Un développeur backend doit pouvoir, sans question résiduelle :

  1. Comprendre le flow annuel de déclaration FATCA + CRS d’un FFI tenant.
  2. Implémenter chaque sous-module avec ses contrats, son schema d’erreurs, ses idempotency keys.
  3. Câbler les règles de classifier déterministes (DSL Kotlin, versionnées YAML).
  4. Câbler les adapters core banking pour récupérer les soldes en fin d’année + paiements.
  5. Générer, signer et soumettre les XML conformes — y compris corrections et nil reports.
  6. Respecter les obligations conformité (DGI TN, IRS Pub 5124, OECD CRS, retention 10 ans WORM).

Flow annuel nominal (FFI tunisien, exercice civil) :

DateÉtape
2026-01-15Scheduler arme le workflow TcrAnnualReportingWorkflow(year=2025) pour chaque tenant.
2026-01-15→01-31indicia-collector consolide tous les indicia capturés via Form Designer + CPS sur l’exercice.
2026-02-01→02-15classifier-engine classe chaque compte/entité (US person, Passive NFE, etc.) et identifie les controlling persons.
2026-02-15→02-28balance-aggregator collecte soldes 31/12/2025 + paiements 2025 par compte.
2026-03-01→03-10xml-generator produit FATCA + CRS, validation XSD, business rules.
2026-03-10→03-25Compliance officer + DAF signent dual-control XAdES.
2026-03-25submit-adapter soumet à la DGI TN (CRS due 31/05, FATCA due 30/09).
2026-04→09Suivi AR, corrections OECD2 si erreurs, ré-soumission si nécessaire.

SourceNaturePersistance
Form Designer (ADR-007) field type INDICIA_FATCAdéclaratif client à l’onboarding et reviews périodiquespersiste en submission.values chiffré PII
OCR documents (passeport, CNI)lieu de naissance non-déclaratifpersiste après extraction
Adresse de résidence (Form + CPS)indicia US address ou résidence fiscale étrangèreCPS variable client.country
Téléphone (Form)indicia US phone (+1)CPS variable client.phone
Pouvoir et représentation (Form)US POA / signataire USCPS variable client.usPOA
Standing instructions (core banking)virements récurrents vers USadapter core banking
Joint account (core banking)titulaire conjoint USadapter core banking

2.2 Liste figée des indicia FATCA (selon IGA TN 2014, Annexe I)

Section intitulée « 2.2 Liste figée des indicia FATCA (selon IGA TN 2014, Annexe I) »
IndiciaCPS pathEffet présomption
US citizenshipclient.usCitizenUS Person
US birthplaceclient.usBirthUS Person (sauf pièce justifiant de la perte de nationalité)
US current addressclient.usAddressUS Person
US mailing address (incl. PO Box, “in care of”)client.usMailingAddressUS Person
US phone (single phone listed and is US)client.usPhoneUS Person
Standing instructions to USclient.usStandingInstructionsUS Person
US Power of Attorney / signatoryclient.usPOAUS Person
US “in-care-of” or hold-mail address as sole addressclient.usHoldMailUS Person

2.3 Liste figée des indicia CRS (OECD CRS Implementation Handbook)

Section intitulée « 2.3 Liste figée des indicia CRS (OECD CRS Implementation Handbook) »
IndiciaCPS pathEffet
Tax residence country (déclaration client)client.taxResidences[]Reportable Person si résidence ≠ pays de l’IF déclarante
Current address in foreign jurisdictionclient.foreignCurrentAddressindice résidence fiscale
Multiple foreign phone numbersclient.foreignPhones[]indice résidence multiple
Foreign POAclient.foreignPOAindice résidence
Hold-mail / in-care-of in foreign jurisdictionclient.foreignHoldMailindice résidence
POST /v1/tcr/indicia/collect
{ "tenant_id": "TN-BANQUEX", "reporting_period": "2025-01-01/2025-12-31", "scope": "all" }
→ { "collect_id": "...", "account_count": 482000, "indicia_snapshot_ref": "minio://..." }

3. classifier-engine — règles déterministes versionnées

Section intitulée « 3. classifier-engine — règles déterministes versionnées »
enum class FatcaStatus {
SPECIFIED_US_PERSON,
NON_US_PERSON,
REPORTING_FFI,
NON_REPORTING_FFI,
ACTIVE_NFE,
PASSIVE_NFE,
EXEMPT_BENEFICIAL_OWNER
}
enum class CrsStatus {
REPORTABLE_PERSON, // résidence fiscale dans une juridiction CRS partenaire
NON_REPORTABLE,
REPORTING_FI,
NON_REPORTING_FI,
ACTIVE_NFE,
PASSIVE_NFE
}

3.2 DSL Kotlin (réutilise grammaire Risk Engine, ADR-025)

Section intitulée « 3.2 DSL Kotlin (réutilise grammaire Risk Engine, ADR-025) »
val classifierFatca = classifierPolicy {
id = "fatca-tn-banquex-v2.0"
version = "2.0.1"
description = "Classification FATCA, IGA TN modèle 1, IRS Pub 5124"
rule("specified_us_person_individual") {
whenever {
(subject("kind") eq "INDIVIDUAL") and
((field("client.usCitizen") eq true) or
(field("client.usBirth") eq true) or
(field("client.usAddress") eq true) or
(field("client.usMailingAddress") eq true) or
(field("client.usPhone") eq true) or
(field("client.usStandingInstructions") eq true) or
(field("client.usPOA") eq true))
}
then = assignStatus(FatcaStatus.SPECIFIED_US_PERSON)
}
rule("active_nfe_business") {
whenever {
(subject("kind") eq "ENTITY") and
(field("entity.passiveIncomeRatio") lt 0.5) and
(field("entity.passiveAssetRatio") lt 0.5)
}
then = assignStatus(FatcaStatus.ACTIVE_NFE)
}
rule("passive_nfe_default") {
whenever {
(subject("kind") eq "ENTITY") and
not(matched("active_nfe_business")) and
not(matched("reporting_ffi"))
}
then = assignStatus(FatcaStatus.PASSIVE_NFE)
}
// ... 12 règles au total couvrant FATCA Annexe I IGA TN
}

Versionnage : chaque policy a un id + version SemVer + hash SHA-256 + signature Ed25519 dual-control compliance + DGI. Toute évolution exige une nouvelle version (audit).

3.3 Détermination des controlling persons (Passive NFE)

Section intitulée « 3.3 Détermination des controlling persons (Passive NFE) »

Pour toute entité classifiée PASSIVE_NFE, identifier les personnes physiques exerçant un contrôle :

Type de contrôleDéfinition CRSSource
Détention capital ≥ 25 %direct ou indirectRNE (POC poc-rne-connector) + déclaration UBO
Contrôle par autres moyensaccord, contrat, votedéclaration client
Senior managing officialsi aucun contrôle ≥ 25 % identifiéKBIS / déclaration

L’engine produit pour chaque Passive NFE la liste des ControllingPerson avec leur statut FATCA/CRS individuel.

POST /v1/tcr/classify
{ "tenant_id": "...", "snapshot_ref": "minio://...", "policy_id": "fatca-tn-banquex-v2.0", "reporting_period": "2025-..." }
→ { "classification_ref": "minio://...", "classified_accounts": 482000, "specified_us_persons": 87, "passive_nfes": 1240, "audit": {...} }

Champ XMLSource core banking
AccountBalance (au 31/12)enquiry STMT.ENT.BOOK ou équivalent (cf POC Temenos T24)
Payment[Interest] (intérêts versés sur l’exercice)classification écritures par type
Payment[Dividends]classification écritures (titres)
Payment[GrossProceeds/Redemptions]écritures de cession titres
Payment[Other]reste applicable

Format pivot CanonicalAccountSnapshot identique pour tous les core banking. Adapters disponibles ou à faire :

Core bankingAdapterStatut
Temenos T24 / Transacttemenos-connectorPOC livré
Sopra Banking Platformsopra-connectorÀ faire
Oracle FlexCube / FCCfcc-connectorÀ faire
Path Solutions iMALimal-connectorÀ faire
Custom on-prem (CSV / SQL view)csv-connectorÀ faire fallback

Soldes et paiements stockés dans leur devise d’origine (CurrCode ISO 4217). FATCA et CRS acceptent multi-devise sans conversion. La devise est déclarée par compte.


<FATCA_OECD version="2.0">
<MessageSpec>
<SendingCompanyIN>...</SendingCompanyIN>
<TransmittingCountry>TN</TransmittingCountry>
<ReceivingCountry>US</ReceivingCountry>
<MessageType>FATCA</MessageType>
<Warning>...</Warning>
<Contact>...</Contact>
<MessageRefId>{TIN}-{year}-{seq}</MessageRefId>
<ReportingPeriod>2025-12-31</ReportingPeriod>
<Timestamp>2026-03-25T10:00:00</Timestamp>
</MessageSpec>
<FATCA>
<ReportingFI>
<Name>...</Name>
<Address>...</Address>
<DocSpec>...</DocSpec>
<FilerCategory>FATCA601</FilerCategory>
</ReportingFI>
<ReportingGroup>
<Sponsor>...</Sponsor> <!-- optionnel -->
<NilReport>...</NilReport> <!-- mutuellement exclusif AccountReport -->
<AccountReport>
<DocSpec>...</DocSpec>
<AccountNumber>...</AccountNumber>
<AccountHolder>
<Individual>...</Individual> OR <Organisation>...</Organisation>
</AccountHolder>
<SubstantialOwner>...</SubstantialOwner> <!-- pour Passive NFE -->
<AccountBalance currCode="TND">12345.67</AccountBalance>
<Payment>
<Type>FATCA501</Type> <!-- Interest -->
<PaymentAmnt currCode="TND">123.45</PaymentAmnt>
</Payment>
</AccountReport>
</ReportingGroup>
</FATCA>
</FATCA_OECD>

POC poc-fatca-generator couvre déjà la génération conforme.

<CRS_OECD version="2.0">
<MessageSpec>
<SendingCompanyIN>...</SendingCompanyIN>
<TransmittingCountry>TN</TransmittingCountry>
<ReceivingCountry>FR</ReceivingCountry>
<MessageType>CRS</MessageType>
<MessageTypeIndic>CRS701</MessageTypeIndic> <!-- New | Correction | Void -->
<MessageRefId>...</MessageRefId>
<ReportingPeriod>2025-12-31</ReportingPeriod>
<Timestamp>2026-03-25T10:00:00</Timestamp>
</MessageSpec>
<CrsBody>
<ReportingFI>
<ResCountryCode>TN</ResCountryCode>
<Name>...</Name>
<Address>...</Address>
<DocSpec>...</DocSpec>
</ReportingFI>
<ReportingGroup>
<AccountReport>
<DocSpec>...</DocSpec>
<AccountNumber>...</AccountNumber>
<AccountHolder>
<Individual>
<ResCountryCode>FR</ResCountryCode>
<TIN issuedBy="FR">...</TIN>
<Name>
<FirstName>...</FirstName>
<LastName>...</LastName>
</Name>
<Address>...</Address>
<BirthInfo>
<BirthDate>1980-05-15</BirthDate>
</BirthInfo>
</Individual>
OR
<Organisation>
<ResCountryCode>FR</ResCountryCode>
<IN issuedBy="FR">...</IN>
<Name>...</Name>
<Address>...</Address>
<AcctHolderType>CRS101</AcctHolderType> <!-- Passive NFE controlled by Reportable Person -->
</Organisation>
</AccountHolder>
<ControllingPerson>...</ControllingPerson> <!-- pour Passive NFE -->
<AccountBalance currCode="TND">12345.67</AccountBalance>
<Payment>...</Payment>
</AccountReport>
</ReportingGroup>
</CrsBody>
</CRS_OECD>

POC poc-crs-generator (cf /engineering/poc-crs-generator/) couvre les cas standards et nil report.

AspectFATCACRS
ReceivingCountrytoujours USpays partenaire variable (résidence du Reportable Person)
AcctHolderTypenonobligatoire pour Passive NFE (CRS101/CRS102/CRS103)
MessageTypeIndicnonobligatoire (CRS701 New, CRS702 Correction, CRS703 Void)
Bilatéral vs multilatéralbilatéral USmultilatéral (1 fichier par pays receveur)
TIN obligatoireoui pour USrecommandé fortement, manque toléré dans certains cas
Fréquenceannuelleannuelle

NiveauVérifications
XSDconformité schéma — toute violation est un rejet immédiat
Business rulesTIN valide, dates cohérentes (ReportingPeriod ≤ Timestamp ≤ now), totals balance ≥ 0 ou flag négatif autorisé, présence ControllingPerson sur Passive NFE, MessageRefId unique

Exigence DGI Tunisie : signature électronique XAdES (Tunisie utilise XAdES-BES sur le fichier final). Clé tenant via Vault + KMS.

Le workflow Temporal exige 2 signatures humaines (Compliance Officer + DAF) avant submit-adapter. Pas de soumission auto.


7.1 DGI Tunisie (FATCA via IGA modèle 1, et CRS local)

Section intitulée « 7.1 DGI Tunisie (FATCA via IGA modèle 1, et CRS local) »

Portail HTTPS DGI : POST authentifié + fichier signé XAdES. Adapter HTTP custom (pas d’API standardisée publique avant 2024 ; à confirmer auprès de DGI fin 2026).

7.2 IRS IDES (alternative pour FFI directement reporting type IGA 2)

Section intitulée « 7.2 IRS IDES (alternative pour FFI directement reporting type IGA 2) »

SFTP avec encryption M3M (chiffrement du fichier avant envoi avec certificate IDES public). Adapter SFTP+OpenSSL.

Après soumission, polling sur le portail (DGI) ou récupération SFTP (IRS) du fichier AR :

  • Accepted : OK, archivé.
  • AcceptedWithErrors : warnings non bloquants. À examiner.
  • Rejected : analyser, corriger, soumettre OECD2 (CRS) ou amendement (FATCA).
TypeFATCACRS
Correction d’un AccountReportnouveau MessageRefId + DocTypeIndic OECD2 + référence DocRefId d’origineidem, MessageTypeIndic = CRS702
Annulation totaleDocTypeIndic OECD3 VoidMessageTypeIndic = CRS703
Resubmit (rejet original)nouveau MessageRefId + reprend les comptes corrigésidem

ArtefactStorageRetentionHash
Snapshot indicia (par exercice)MinIO tcr/{tenant}/{year}/indicia.json.enc10 ans WORMSHA-256 dans audit ledger
Classification resultMinIO tcr/{tenant}/{year}/classification.json.enc10 ans WORMidem
Balance snapshotMinIO tcr/{tenant}/{year}/balances.json.enc10 ans WORMidem
XML FATCA généré + signéMinIO tcr/{tenant}/{year}/fatca.xml.signed10 ans WORMhash + sig
XML CRS par paysMinIO tcr/{tenant}/{year}/crs/{country}.xml.signed10 ans WORMhash + sig
Acknowledgement ReceiptsMinIO tcr/{tenant}/{year}/ar/{filename}10 ans WORMidem
Audit ledgerPostgreSQL append-only10 ans WORMchaque ligne hashée chained

MéthodeEndpointDescription
POST/v1/tcr/workflows/rundéclenche manuellement un workflow annuel pour un tenant et une période
GET/v1/tcr/workflows/:idsuit l’avancement
GET/v1/tcr/workflows/:id/audittrace complète
POST/v1/tcr/signsignature dual-control d’un XML prêt
POST/v1/tcr/submitsoumet à la DGI ou IRS
POST/v1/tcr/correctiondémarre un workflow de correction OECD2

OpenAPI complet : voir /api/openapi/ section TCR.


MetricMVPV2
Génération XML 10 K comptes≤ 30 s≤ 10 s
Génération XML 1 M comptes≤ 8 min≤ 3 min
Soumission DGI (round-trip portal) p95≤ 60 s≤ 30 s
AR polling fréquencetoutes les 30 minwebhook DGI si disponible
Validation XSD pass rate100 %100 %
AR rejected rate≤ 5 %≤ 1 %

  • Données : TIN, soldes, paiements = SPI. Chiffrement enveloppe AES-256-GCM, KEK per-tenant. Pas de log clair.
  • Accès : seul tcr-svc lit les balances ; les rôles compliance et DAF accèdent via UI signée.
  • Audit : chaque accès tracé avec actor, action, timestamp, dataRef, signature.
  • Signatures : Ed25519 sur le hash du XML après XAdES, double signature obligatoire.
  • Transmission : DGI portail HTTPS + cert pinning ; IRS SFTP + encryption M3M.
  • DPIA : traitement légal obligation légale (RGPD art. 6.1.c). Pas de consentement requis. Cf DPIA.

RéférenceExigenceCouverture
IGA TN-USA modèle 1 (2014)classification + déclaration via DGIclassifier + xml-gen + submit DGI ✓
IRS Pub 5124 v2.0XML schema FATCAxml-generator FATCA ✓
OECD CRS XML User Guide v2.0XML schema CRSxml-generator CRS ✓
Loi 2016-71 (Tunisie CRS)échange automatique d’informationsclassifier + soumission DGI ✓
DGI TN procéduresdépôt portail signé XAdESsubmit-adapter DGI ✓
§6041 / §1471 IRCaccuracy reportingdual-control + retention 10 ans ✓
RGPD art. 6.1.cobligation légaleDPIA ✓

ItemMVPV2
Adapters core bankingTemenos T24+ Sopra + FCC + iMAL
Submit DGIportail manuel + automatisé HTTPAPI officielle si publiée
Submit IRS IDESsi tenant IGA 2full automation
OECD partenaires CRS supportésTOP 30 (couvre 95 % volumes TN)tous
Sponsor modelnonoui
Reporting period non-civilnonoui
Alimentation auto FATCA W-8/W-9 formsnonoui

  • DSL classifier compilé sur 12 règles FATCA + 8 règles CRS, signé policy v2.0.0
  • indicia-collector consume Form Designer events + CPS sans drift
  • balance-aggregator Temenos T24 valide sur 100 K comptes pilote
  • xml-generator FATCA + CRS validés XSD sur 50 cas tests
  • Signature XAdES-BES opérationnelle avec KMS Vault
  • Dual-control workflow approuvé compliance + DAF
  • Submit-adapter DGI testé sur sandbox DGI 2026
  • AR polling + corrections OECD2 fonctionnels
  • Storage WORM 10 ans configuré sur MinIO
  • Audit ledger PostgreSQL append-only signé
  • DPIA mise à jour
  • Runbook on-call : flow incident soumission (portail down, AR rejected)


Document de spec pipeline TCR — version 1.0 (2026-04-29). Mises à jour bloquantes nécessitent un ADR.