i
Iris · operare manuală
Operator contabil
Tenant: BONO
OC
Pasul 0

Modelul canonic de date

Draft v1 · 24 August 2026 · strawman pentru shaping cu Prodi + Cristi · sursa: iris-modele-de-date.md

24 August 2026 · Draft v1 (strawman pentru sesiunea de shaping cu Prodi + Cristi) · BONO × SOLO · Confidențial Autor: BGN + Claude · Răspunde concluziei review-ului de arhitectură („v1 e formă — lipsesc tipurile"). Din acest model derivă: contractele Tenant-in/Tenant-out, porturile motoarelor, schema DB, tipurile C# și fixtures-urile de test.


0. Principiile modelului (deciziile BGN, 24 aug)

  1. Tipul de document ≠ formatul sursă. Patru tipuri canonice — Factură · Bon · Extras de cont · Factură emisă — și un atribut separat de format (xml_efactura | pdf | imagine | json) care alege pipeline-ul de extracție, nu structura datelor. eFactura = o factură sosită ca XML; factura „externă" = factura cu extensia de valută populată.
  2. Unitatea interpretabilă are două forme. Facturile/bonurile se descompun în linii; extrasul în operațiuni bancare — concept comun abstract, două specializări. Tot fluxul validat în prototip (interpretare per unitate → tranzacția emerge → Descrierea contabilă → nota) rulează identic peste ambele.
  3. Tranzacția emerge din interpretare — nu se „face" grupare: unitățile cu tratament contabil identic (categorie + folosință + cotă TVA) formează automat o tranzacție; o tranzacție = o notă contabilă.
  4. Zero-pierderi pe două niveluri: canonicul conține tot ce folosește contabilitatea; garanția completitudinii o dă păstrarea sursei (fișier original în R2 + parsarea brută în raw JSONB). Câmpurile noi se promovează aditiv din raw, cu backfill.
  5. Linia agregată e legală exact până unde tratamentul e omogen (bon cu 23 de produse de protocol = o linie; dacă un produs diferă ca tratament, se sparge în minimul necesar). Linia agregată = o tranzacție pre-grupată la introducere.
  6. Matching-ul e modulul intern nr. 4 (port IMatching): corelarea operațiunilor bancare cu documentele justificative, many-to-many cu sume, manual-first cu sugestii deterministe. Nota de decontare atârnă de corelare, nu de document.
  7. PFA e în model de la început: nota contabilă are două ramuri (SRL = partidă dublă; PFA = categorii generale); ramura PFA există în schemă acum, valorile regulilor se populează când sosesc de la Cristi.
  8. Transversal: confidence nullable peste tot (gol în Faza 1, populat de motoare în Faza 2) · sursa deciziei pe orice câmp interpretat (operator | istoric | larry@vX | adam@vX | match@vX) · schimbări doar aditive · sume DECIMAL, niciodată float · cota TVA e per linie, dată din document (21% azi, 19% pe documente vechi — nu constantă globală).

1. Vocabularul — entitățile, dintr-o privire

Tenant ─┬─ ProfilClient ──(înghețat la procesare)── ContextSnapshot
        │
        └─ Document (nucleu comun + extensie per tip + raw)
              ├── LinieDocument      ─┐   (unități interpretabile)
              ├── OperațiuneBancară  ─┤
              │        └─ Interpretare (categorie · folosință · verdict · sursă · confidence)
              ├── Tranzacție (emergentă) ── DecizieContabilă ── NotaContabilă (SRL | PFA)
              ├── Corelare (operațiune ↔ document/tranzacție, sume alocate) ── NotaDecontare
              └── Dubiu (junior → senior)
Eveniment (jurnal append-only — sursa adevărului pentru toate de mai sus)
MemoryIndex (proiecție: istoricul per furnizor → precompletare)

2. Document — nucleul comun (toate tipurile)

CâmpTipNote
iris_document_idUUIDv7PK intern (Guid.CreateVersion7())
display_idtextBN-2026-00003 — secvență per (tenant, an-ingest), NU gapless
tenant_idUUIDdin claim-ul JWT validat (iam); RLS pe el
client_idUUIDfirma clientului final (ref ProfilClient)
source_document_idtextID-ul în sistemul tenantului (Nucleu/Jedi)
tipenumfactura · bon · extras · factura_emisa
format_sursaenumxml_efactura · pdf · imagine · json
statusenumprimit · in_lucru · in_asteptare · finalizat · respins (+ motiv la respins)
hash_fisierbyteadedup identic per tenant
fisier{r2_key, mime, size}originalul, prefix per tenant
numar_doc, data_doctext, datede pe document (extras: n/a — vezi extensia)
monedachar(3)per document; RON implicit
curs_bnr, data_cursdecimal, datedoar valută ≠ RON (sursa: forex.bono.ro)
total_net, total_tva, totaldecimal(14,2)totalurile de pe document — verificarea încrucișată cu unitățile e obligatorie la finalizare
mentiunitextmențiuni la nivel de document
context_snapshot_idUUIDcontextul imutabil folosit
dedup_semantic_refUUID?posibil duplicat (nr+dată+CUI+sumă) → flag, nu respingere
rawJSONBparsarea brută completă (D5) — XML-ul UBL descompus, JSON-ul primit etc.
trace_id, timestamps, operator_idtransversale

3. Extensiile per tip

3.1 Factură (primită) — ext_factura

CâmpNote
partenersnapshot: {nume, cui_sau_vat_id, tara, adresa, tip: PJ/PF} — furnizorul
verificare_partenerrezultat directory.bono.ro: {anaf_activ, platitor_tva, vies_valid, verificat_la}; nullable dacă serviciul e jos (flag neverificat)
scadenta, mod_platadate, enum (OP/card/cash/—)
taxare_inversa_mentiunibool + text — mențiunile reverse-charge de pe document
rol_clientfix beneficiar

*Factura „externă" nu e tip separat*: moneda ≠ RON și/sau partener.tara ≠ RO activează câmpurile de valută din nucleu + regulile de scenariu TVA (TVA-SERV / TVA-AIC / TVA-IMP).

3.2 Bon — ext_bon

CâmpNote
partenerca la factură (emitentul bonului)
cui_pe_bonbool — CUI-ul firmei clientului apare pe bon (relevanță fiscală)
mod_plataenum

Fără serie/scadență. Liniile agregate (D6) sunt tipice aici.

3.3 Factură emisă — ext_factura_emisa

CâmpNote
beneficiarsnapshot partener — clientul firmei (rolurile inversate)
serie_numar_fiscalnumerotarea fiscală a tenantului (nu a noastră)
sursa_emiterefacturare_bono · oblio · smartbill · alt (via SPV toate ajung oricum)
rol_clientfix furnizor

Interpretarea: categorie de venit (grupa V), fără folosință/deductibilitate (N/A). Nota: ramura de venit (ex. 4111 = 704).

3.4 Extras de cont — ext_extras

CâmpNote
iban, bancacontul
perioada{de_la, pana_la}
sold_initial, sold_finaldecimal — verificare încrucișată: sold_initial + Σ(operațiuni semnate) = sold_final (echivalentul „liniile bat cu totalurile")
moneda_contpoate diferi de RON

numar_doc/data_doc din nucleu: n/a — identitatea extrasului e (iban, perioadă).


4. Unitățile interpretabile

4.1 Comun (ambele specializări)

unit_id (UUIDv7) · document_id · ordinal (poziția în sursă) · descriere · interpretare (secțiunea 5) · tranzactie_id (derivat — secțiunea 6) · status (de_interpretat · interpretata · in_dubiu)

4.2 LinieDocument (factură, bon, factură emisă)

CâmpNote
cantitate, um, pret_unitarum: buc/ore/kg/luni… (ajută bun vs. serviciu)
valoare_neta, cota_tva, valoare_tva, valoare_totalacota per linie (21/19/11/9/5/0), din document
reducerediscount pe linie
mentiune_tva_linie„scutit", „taxare inversă" per linie
agregatabool + nr_articole? — linia agregată (D6); permisă doar cu tratament omogen
alocarebool — linie de livrare/transport/discount distribuită proporțional cu neta pe celelalte tranzacții; nu primește interpretare proprie; breakdown-ul alocării se stochează pe tranzacții (audit)

4.3 OperațiuneBancară (extras)

CâmpNote
data_operatiunii, data_valuteidate
sensdebit · credit (plată/încasare)
sumadecimal, pozitivă; semnul îl dă sensul
sold_dupadecimal — pentru verificarea lanțului
contrapartida{nume?, iban?, cui?} — ce se poate extrage
referintatextul brut (detaliile plății) — inputul principal al interpretării și al matching-ului
tip_operatiunesugerat: transfer · pos · comision · atm · fx · sepa … (listă de închis la shaping)

5. Interpretarea (per unitate)

CâmpNote
categoriecod din taxonomia globală versionată (~58 categorii, 8 grupe + grupa V venituri)
folosinta_firmaDA · NU — doar la cheltuieli; NU → R1 → 0%
verdict_operatiunedoar operațiuni (D3): directa (cheltuială/venit propriu → notă) · de_corelat (plată/încasare cu document justificativ → Corelare sau export marcat) · transfer_intern (între conturile proprii / neutru → fără notă)
confidence_categorie, confidence_folosinta0–100, nullable (Faza 1: gol)
explicatieraționamentul (română) — de la om la corecții, de la Larry în Faza 2
sursaoperator · istoric · larry@vX + operator_id/versiuni
corecțiilenu se suprascriu — eveniment interpretare.corectata cu valoarea veche/nouă/motiv

6. Tranzacția (emergentă) + Decizia contabilă

Tranzacție: tranzactie_id ({doc}-T{n}) · cheia de tratament (categorie, folosinta, cota_tva) · unitati[] · suma_alocata_primita (partea din liniile de alocare, cu breakdown per linie-sursă) · status (draft · interpretata · nota_generata · exportata).

DecizieContabilă (per tranzacție — precompletată determinist, confirmată de om în Faza 1):

CâmpNote
scenariu_tvaNO-TVA · TVA-SERV · TVA-AIC_BUNURI · TVA-IMP_BUNURI (+ validările G1–G3 ca flags)
ded_ch_pct, ded_tva_pctdin reguli (anexa Adam): fix / R1 / R3(regim auto) / R4(sediu) / N/A
baza_ch, baza_tvaformulele per scenariu (NO-TVA la neplătitor: TVA intră în cost; IMP: editabil pe DVI)
val_ded_ch, val_neded_ch, tva_ded, tva_nededaritmetică, needitabile direct
amortizare{da/nu, luni} — doar marcaj; calculul lunar = Engine Contabil (tenant)
tac, tac_tvacodurile de tratament
flags[]G1_vies_invalid · G2_extern_peste_prag · G3_bun_extern_special · prag_mf_reclasificare · prag_mf_verificare · divergenta_tratament · neverificat_anaf …
reguli_aplicate[]id-urile regulilor + ruleset_version — trasabilitate completă

7. Nota contabilă — două ramuri (D4)

Comun: nota_id · tip_nota (nota_document — din tranzacție · nota_decontare — din corelare) · sursa (tranzactie_id | corelare_id) · ruleset_version · context_snapshot_id · versiuni motoare (nullable) · autor · timestamps.

Ramura SRL (partidă dublă): linii_nota[] = {cont_debit, cont_credit, suma, eticheta} — ex: [{603, 401, 2.979,10}], la taxare inversă + {635, 4423, …}; decontare: {401, 5121, …} / încasare {5121, 4111, …}.

Ramura PFA (fără partidă dublă): inregistrari[] = {categorie_generala, suma, deductibilitate_pct, sens: cheltuiala|venit}. *Schema există de acum; maparea categorie→înregistrare PFA se populează la sosirea tabelului de la Cristi (gap cunoscut).*

8. Corelarea (D3) — modulul Match

CâmpNote
corelare_idUUIDv7
operatiune_idoperațiunea bancară
tinta{document_id sau tranzactie_id, tip: factura_primita · factura_emisa}
suma_alocatamany-to-many cu sume: o plată poate stinge N facturi; o factură poate fi plătită în N tranșe; Σ alocărilor ≤ suma operațiunii și ≤ restul de plată al țintei
statussugerata · confirmata · respinsa
confidencenullable; sugestiile deterministe: sumă ± toleranță, fereastră de zile, CUI/nume contrapartidă, numărul facturii în referință
sursaoperator · match@vX
nota_decontare_idnota generată la confirmare

Operațiunile de_corelat fără țintă în Iris (salarii, taxe, rate) → export cu marcaj de_corelat_la_tenant.

9. Contextul: ProfilClient + ContextSnapshot

ProfilClient (starea curentă per (tenant, client), actualizată prin API-ul de ingest — sursă: Nucleu/Vertigo; de aliniat cu modelul Vertigo al lui Cristi): identitate {nume, cui} · tip_entitate (SRL · PFA) · regim_impozitare (micro · profit · pfa_real · pfa_norma) · TVA {platitor, cod_activ, vies_valid} · asociati[], administratori[] (nume — detectarea facturilor pe persoană fizică) · regim_auto (fara · mixt · 100%) · activitate_la_sediu (bool) · mijloace_fixe[] · caen_principal, caen_secundare[] · nr_salariati · versiune_profil · goluri[] (câmpurile lipsă — raportate asincron tenantului).

ContextSnapshot (imutabil, per document): copia profilului la momentul procesării + rezultatele interogărilor externe pe partener + versiune_profil referită. Orice decizie contabilă se leagă de snapshot, nu de profilul curent.

10. Tenant (config minimal)

tenant_id · nume (BONO, SOLO) · iam_client_ref (identitatea la iam — Iris nu ține secrete de auth) · webhook {url, hmac_secret_ref} (secretul în Railway secrets) · tipuri_acceptate[] · feature_flags{} (per categorie — mecanismul shadow→precompletare→Zero-Touch) · prefix_display (BN, SL).

11. Memoria (proiecție, nu entitate-sursă)

(tenant_id, client_id, partener_cui, descriere_normalizata) → {categorie, folosinta, tratament, count, ultima_data} — construită din evenimentele de interpretare confirmată; alimentează precompletarea (nivelul 0 al piramidei) și typeahead-ul de furnizori. Reconstruibilă oricând prin replay.

12. Stocarea în Postgres (propunere)

TabelNote
documentsnucleul + extensia în coloane tipizate unde e financiar + ext JSONB pentru rarități + raw JSONB; partiționat lunar; RLS pe tenant_id
document_lines / bank_operationsdouă tabele (nu unul cu NULL-uri); interpretarea inline (coloanele §5)
transactions+ decizia contabilă inline (coloanele §6) + breakdown alocări JSONB
notescu linii_nota/inregistrari JSONB (forme diferite SRL/PFA)
correlations§8
client_profiles, context_snapshots§9
tenants§10
eventsappend-only, partiționat lunar — sursa adevărului
outboxlivrări webhook
display_sequences(tenant_id, an) → next — mecanica Prodi
memory_indexproiecția §11
dubiiîntrebare/răspuns junior↔senior, ref unitate/tranzacție

Toate tabelele multi-tenant: tenant_id NOT NULL + politică RLS + scoping aplicativ (dublu strat).

13. Fixtures de aur (setul inițial — din exemplele Cristi + prototipuri)

  1. eFactură Roz SRL → Deko Vibe: 1 linie, F4 Publicitate, NO-TVA, 100% → 6232 = 401
  2. Bon OMV → Pixel Forge: 1 linie, A8 Combustibil, R3 (Mixt) 50% → 6022 = 401 + valori ded/neded
  3. Factură externă Anthropic (USD): TVA-SERV taxare inversă, curs BNR → 628 = 401 + 635 = 4423
  4. Factură emisă servicii: grupa V → 4111 = 704 (ramura venit)
  5. Extras ING — comision: operațiune directa, G2 Comisioane → 627 = 5121
  6. Extras ING — plată IKEA: operațiune de_corelat → Corelare cu factura IKB-78421 (sumă integrală) → notă decontare 401 = 5121
  7. Factura IKEA multi-linie (din prototip): 4 linii (2×D2 agregabile + 1 cu folosință NU + 1 alocare) → 2 tranzacții + alocare proporțională + flag divergență — testul complet al emergenței
  8. Bon Kaufland agregat (D6): linie agregată 22 articole protocol + linie separată nejustificat

Fiecare fixture = JSON complet pe tot lanțul (document → unități → interpretări → tranzacții → decizii → note) — devine test de contract în CI și, ulterior, eval pentru Larry.

14. Întrebări deschise pentru sesiunea de shaping

  1. Maparea PFA (categorie → înregistrare) — ramura există, valorile la Cristi
  2. Lista tip_operatiune bancară de închis + sursele de parsare extras (PDF per bancă? MT940/camt.053 unde există?) — formatul extraselor per bancă e cel mai variabil teren
  3. Toleranțele matching-ului (sumă ±?, fereastră zile?) și pragul de sugestie
  4. Alinierea ProfilClient ↔ modelul Vertigo (istoric per atribut — materialele cerute lui Cristi)
  5. Contractul Nucleului (Prodi): forma exactă a source_document_id + cine trimite interim
  6. Taxonomia: confirmarea listei ~58 + grupa V (venituri) ca extensie a anexei Adam