|
| 1 | +# SMB tenant — schema reference for lance-graph sessions |
| 2 | + |
| 3 | +> **Why this folder exists:** lance-graph is the spine; sessions |
| 4 | +> working here usually don't have SMB context loaded. When changes to |
| 5 | +> `lance-graph-ontology` / `lance-graph-callcenter` / `lance-graph-rbac` |
| 6 | +> affect tenant consumers, this folder shows what the SMB tenant |
| 7 | +> (`AdaWorldAPI/smb-office-rs`) will hydrate into the registry — so |
| 8 | +> upstream changes can be evaluated against a real consumer shape |
| 9 | +> without cross-repo grepping. |
| 10 | +> |
| 11 | +> **Authority:** the source of truth is smb-office-rs: |
| 12 | +> - BSON-shape: `crates/smb-ontology/src/{customer,rechnung,mahnung, |
| 13 | +> schuldner,woa_artikel,remaining}.rs` |
| 14 | +> - Foundry-shape stopgap: `crates/smb-realtime/src/ontology.rs:: |
| 15 | +> build_smb_ontology()` |
| 16 | +> - Originating inventory: `smb-office-rs/.claude/board/OGIT_TTL_INVENTORY.md` |
| 17 | +> |
| 18 | +> When the two diverge, smb-office-rs wins; update this doc. |
| 19 | +
|
| 20 | +--- |
| 21 | + |
| 22 | +## 1. Two ontology surfaces (by design) |
| 23 | + |
| 24 | +| Surface | Naming | Entity count | Purpose | Where it lives | |
| 25 | +|---|---|---|---|---| |
| 26 | +| **BSON-shape** | German wire names, `smb.<entity>` prefix | 14 | Storage layer; matches C# `db_*.cs` verbatim per smb-office-rs CLAUDE.md iron rule 1 | `smb-ontology` crate | |
| 27 | +| **Foundry-shape** | English names, no prefix | 3 | API/membrane layer; what RBAC + RLS + Foundry Object Explorer surface externally | `smb-realtime::ontology` (consumer-side stopgap until OGIT TTL hydrates) | |
| 28 | + |
| 29 | +Foundry shape is a **projection** of BSON shape. One Foundry `Customer` |
| 30 | +projects from one `smb.customer` row. Translation lives in |
| 31 | +`smb_bridge::orchestration` (entity_type → table_name). |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +## 2. Foundry-shape — what `OntologyDto::project(®istry, "SMB", …)` must return |
| 36 | + |
| 37 | +Mirrors the body that lance-graph PR #355 deleted from |
| 38 | +`crates/lance-graph-callcenter/src/ontology_dto.rs::smb_ontology()`. |
| 39 | +The consumer-side stopgap at `smb-realtime/src/ontology.rs:: |
| 40 | +build_smb_ontology()` reproduces it verbatim. |
| 41 | + |
| 42 | +### Entities (3) |
| 43 | + |
| 44 | +| Public name | OGIT URI | Required | Optional (Passthrough) | Searchable (CamPq) | Free | |
| 45 | +|---|---|---|---|---|---| |
| 46 | +| `Customer` | `ogit.SMB:Customer` | `customer_name`, `tax_id` | `address`, `iban` | `industry` | `note` | |
| 47 | +| `Invoice` | `ogit.SMB:Invoice` | `invoice_number`, `date`, `total_amount`, `currency`, `customer_ref` | `due_date` | — | `note` | |
| 48 | +| `TaxDeclaration` | `ogit.SMB:TaxDeclaration` | `declaration_id`, `tax_year`, `customer_ref`, `declaration_type` | `filing_date`, `status` | — | — | |
| 49 | + |
| 50 | +### Links (`one_to_many`, 2) |
| 51 | + |
| 52 | +- `Customer` —`issued`→ `Invoice` |
| 53 | +- `Customer` —`filed`→ `TaxDeclaration` |
| 54 | + |
| 55 | +### Actions (3) |
| 56 | + |
| 57 | +- `approve` (manual) on `Invoice` → predicate `status` |
| 58 | +- `classify` (auto) on `Customer` → predicate `industry` |
| 59 | +- `submit` (manual) on `TaxDeclaration` → predicate `status` |
| 60 | + |
| 61 | +### Label (bilingual) |
| 62 | + |
| 63 | +- `de` → `Steuerberatungskanzlei` |
| 64 | +- `en` → `Tax Practice` |
| 65 | + |
| 66 | +--- |
| 67 | + |
| 68 | +## 3. BSON-shape — 14 entities under `smb.*` |
| 69 | + |
| 70 | +German wire names match C# `db_*.cs` verbatim. Per-property markings |
| 71 | +carry GDPR / financial classification. |
| 72 | + |
| 73 | +| Schema name | Required predicates | Marking dominance | Source | |
| 74 | +|---|---|---|---| |
| 75 | +| `smb.customer` | `kdnr`, `firma` | Pii on personal, Financial on bank | `customer.rs` | |
| 76 | +| `smb.schuldner` | `schuldner_id`, `debtor_name`, `saldo` | Internal/Pii/Financial mix | `schuldner.rs` | |
| 77 | +| `smb.rechnung` | `rechnungsnr`, `kunde_kdnr`, `rechnungsdatum`, `faellig_am`, `betrag_netto`, `betrag_brutto`, `ust_betrag`, `ust_satz`, `zahlungsstatus` | Financial on amounts | `rechnung.rs` | |
| 78 | +| `smb.mahnung` | `mahnung_id`, `schuldner_id`, `rechnungsnr`, `stufe`, `ausgestellt_am`, `faellig_am`, `gesamtsumme`, `status` | Internal | `mahnung.rs` | |
| 79 | +| `smb.dokument` | `dokument_id`, `parent_entity`, `parent_id`, `dateiname`, `mime_type`, `hochgeladen_am` | Internal | `remaining.rs` | |
| 80 | +| `smb.bank` | `buchungssatz_id`, `kontonummer`, `buchungsdatum`, `valutadatum`, `betrag`, `waehrung` | Financial | `remaining.rs` | |
| 81 | +| `smb.fibu` | `buchung_id`, `buchungsdatum`, `soll_konto`, `haben_konto`, `betrag` | Financial | `remaining.rs` | |
| 82 | +| `smb.steuer` | `steuer_id`, `steuerart`, `zeitraum_von`, `zeitraum_bis`, `bemessungsgrundlage`, `steuerbetrag`, `status` | Financial | `remaining.rs` | |
| 83 | +| `smb.lieferant` | `lieferant_id`, `firma` | Internal | `remaining.rs` | |
| 84 | +| `smb.mitarbeiter` | `mitarbeiter_id`, `vorname`, `nachname` | Pii on personal, Financial on iban | `remaining.rs` | |
| 85 | +| `smb.auftrag` | `auftrag_id`, `kunde_kdnr`, `eingegangen_am`, `auftragssumme`, `status` | Internal | `remaining.rs` | |
| 86 | +| `smb.angebot` | `angebot_id`, `kunde_kdnr`, `erstellt_am`, `angebotssumme`, `status` | Internal | `remaining.rs` | |
| 87 | +| `smb.zahlung` | (see `remaining.rs:503+`) | Financial | `remaining.rs` | |
| 88 | +| `smb.artikel` | `beschreibung` | Internal | `woa_artikel.rs` (WoA tenant) | |
| 89 | + |
| 90 | +All entities also carry `fingerprint` (`CodecRoute::CamPq`) and audit |
| 91 | +fields (`ad_create`, `ad_modify`, `ad_delete`). |
| 92 | + |
| 93 | +--- |
| 94 | + |
| 95 | +## 4. Projection rules — Foundry shape ← BSON shape |
| 96 | + |
| 97 | +| Foundry property | BSON source | |
| 98 | +|---|---| |
| 99 | +| `Customer.customer_name` | `smb.customer.firma` | |
| 100 | +| `Customer.tax_id` | `smb.customer.umsatzsteuer_id` (preferred) or `steuernummer` | |
| 101 | +| `Customer.address` | `smb.customer.anschrift` (+ `plz` + `ort` + `land` joined) | |
| 102 | +| `Customer.iban` | `smb.customer.iban` | |
| 103 | +| `Customer.industry` | `smb.customer.branche` | |
| 104 | +| `Customer.note` | `smb.customer.notiz` | |
| 105 | +| `Invoice.invoice_number` | `smb.rechnung.rechnungsnr` | |
| 106 | +| `Invoice.date` | `smb.rechnung.rechnungsdatum` | |
| 107 | +| `Invoice.total_amount` | `smb.rechnung.betrag_brutto` | |
| 108 | +| `Invoice.currency` | `smb.rechnung.waehrung` | |
| 109 | +| `Invoice.customer_ref` | `smb.rechnung.kunde_kdnr` | |
| 110 | +| `Invoice.due_date` | `smb.rechnung.faellig_am` | |
| 111 | +| `Invoice.note` | `smb.rechnung.notiz` | |
| 112 | +| `TaxDeclaration.declaration_id` | `smb.steuer.steuer_id` | |
| 113 | +| `TaxDeclaration.tax_year` | derived from `smb.steuer.zeitraum_von` (year part) | |
| 114 | +| `TaxDeclaration.customer_ref` | **gap** — `smb.steuer` lacks a customer column; planned: add `kunde_kdnr` | |
| 115 | +| `TaxDeclaration.declaration_type` | `smb.steuer.steuerart` | |
| 116 | +| `TaxDeclaration.filing_date` | **gap** — `smb.steuer` lacks a filing date column; planned: add `eingereicht_am` | |
| 117 | +| `TaxDeclaration.status` | `smb.steuer.status` | |
| 118 | + |
| 119 | +Both gaps are smb-ontology-side fixes, independent of OGIT TTL. |
| 120 | + |
| 121 | +--- |
| 122 | + |
| 123 | +## 5. What lance-graph changes affect SMB |
| 124 | + |
| 125 | +| Lance-graph change | Affects SMB how | |
| 126 | +|---|---| |
| 127 | +| `OntologyDto::project(®istry, namespace, …)` signature | `smb-realtime::dto::smb_dto(locale)` calls it for `namespace = "SMB"`. Changes to the projection contract break the consumer. | |
| 128 | +| `MappingRow` columns (e.g. PR #355 `IdentityCodec`, `QualiaMeta`) | Computed at hydration; SMB doesn't author them but consumes the projected `OntologyDto` fields. | |
| 129 | +| `SchemaExpander` trait | smb-realtime's SPO bridge (`expand_smb_entity`) uses `Ontology::expand_entity`. If SchemaExpander moves to `MappingRow`, smb-realtime adapts. | |
| 130 | +| `CachedOntology::new(Ontology)` legacy path | Currently retained for smb-realtime's `smb_cached_ontology()`. If deprecated, smb-realtime needs `CachedOntology::from_registry(&OntologyRegistry, namespace)`. | |
| 131 | +| `Policy::evaluate(role, entity, Operation)` | smb-realtime's `SmbMembraneGate` (PR #29) wraps this. Entity keys passed are the Foundry-shape names (`Customer`, `Invoice`, `TaxDeclaration`). | |
| 132 | +| `Locale` enum additions | smb-realtime's DTO cache projects every `Locale` eagerly; growing the enum forces cache init updates. | |
| 133 | +| TTL hydrator (`hydrate_once_sync`) input format | Decides what OGIT TTL must carry for SMB (see Section 2 for the shape). | |
| 134 | + |
| 135 | +--- |
| 136 | + |
| 137 | +## 6. Stale on the consumer side (will retire when registry hydration lands) |
| 138 | + |
| 139 | +When `OntologyRegistry::hydrate_once_sync(ttl_root)` populates the |
| 140 | +`"SMB"` namespace from real OGIT/NTO/SMB TTL: |
| 141 | + |
| 142 | +- `smb-realtime/src/ontology.rs::build_smb_ontology()` — body deletes, |
| 143 | + becomes a thin `OntologyDto::project(®istry, "SMB", …)` facade. |
| 144 | +- `smb-realtime/src/dto.rs::cached_smb_ontology() -> &'static Ontology` |
| 145 | + — retired when `SchemaExpander` learns to project from `MappingRow`. |
| 146 | +- `smb-realtime/src/transcode.rs::smb_cached_ontology()` — retired when |
| 147 | + `CachedOntology::from_registry` ships. |
| 148 | +- Dual cache path in `smb-realtime/src/spo.rs` (`#[cfg(feature = "postgrest")]`) |
| 149 | + — collapses to single path when `dto` becomes unconditional. |
| 150 | + |
| 151 | +--- |
| 152 | + |
| 153 | +**Last updated:** 2026-05-12, pairs with smb-office-rs commit `611c147`. |
| 154 | +Update when SMB schema changes in smb-ontology or when lance-graph's |
| 155 | +registry projection contract shifts. |
0 commit comments