Data Model — Per-Building BOM Dictionary from Rosetta Stones¶
Foundation: BBC · BIM_COBOL · MANIFESTO · TestArchitecture · ACTION_ROADMAP · SourceCodeGuide
This specification governs the creation of {PREFIX}_BOM.db dictionaries.
Each building has its own BOM dictionary (SH_BOM.db, DX_BOM.db), reproduced from fresh
by the IFCtoBOM Java pipeline: ./scripts/run_RosettaStones.sh classify_sh.yaml.
No hand-editing. No patching. Code produces data.
Source authority:
- TheRosettaStoneStrategy.md §Stage 2: IFCtoBOM pipeline writes m_bom + m_bom_line per building
- BOMBasedCompilation.md §4 Rule 8 (Cheating Maxim): dx/dy/dz MUST be parent-relative, NEVER world-space centroids
- BOMBasedCompilation.md §4: Tack convention — dx/dy/dz = where child's LBD sits in parent (the geometric foundation)
Updated: 2026-03-26
Principle: 4-DB split (validation.db merged into ERP.db).
- component_library.db = product LOD catalog (M_Product, geometry, orientation)
- ERP.db = discipline metadata + compliance rules (schedules, types, placement rules, AD_Val_Rule, AD_Clash_Rule, AD_Occupancy_Class) — see DISC_VALIDATION_DB_SRS.md
- {PREFIX}_BOM.db = per-building spatial arrangement (m_bom + m_bom_line with dx/dy/dz)
- output.db = transactional (fresh each compile)
At compile time, run_RosettaStones.sh creates library/_SH_compile.db (or _DX_compile.db) — a temp copy of {PREFIX}_BOM.db enriched with shared schema + C_DocType. Java reads via -Dbom.db=library/_SH_compile.db. Note: M_Product is transitionally copied to BOM DB for BOMWalker; target: read from library only.
Note: Discipline metadata migration complete (session 41). All discipline tables (ad_space_type, ad_element_mep, ad_wall_face, placement_rules, etc.) now live exclusively in ERP.db. Java code (MEPAD, MEPBOMResolver, ManifestResolver, CalibrationDAO) reads from ERP.db. component_library.db reduced from 81→21 tables (LOD catalog only). See DISC_VALIDATION_DB_SRS.md §6.
1. Extraction Pipeline — I_Element_Extraction → m_bom + m_bom_line¶
1.1 Data Source¶
I_Element_Extraction in component_library.db is the authoritative extraction archive.
Each row is one IFC element with its world-space AABB (min_x/max_x, min_y/max_y, min_z/max_z)
and a resolved M_Product_ID linking to the product catalog.
| Building | building_type | Type |
|---|---|---|
| Sample House | Ifc4_SampleHouse | Single-storey residential |
| Duplex | Ifc2x3_Duplex | Paired duplex, 2-storey |
Element counts and storey breakdowns: see PROGRESS.md gate table.
1.2 Tack Convention¶
See BOMBasedCompilation.md §4 for the full specification.
Every m_bom_line carries (dx, dy, dz) — the 3D position within the parent
where the child's LBD corner sits. LBD = Left-Back-Down = (minX, minY, minZ).
Centroid is never stored in the BOM — it is computed at the output stage only.
World coordinate reconstruction (all 3 axes accumulated):
element_LBD = building_origin + tack_from[1] + tack_from[2] + ... + tack_from[N]
centroid = element_LBD + (width/2, depth/2, height/2)
Invariant: All dx >= 0, dy >= 0, dz >= 0. A child's LBD is always to the right/front/above its parent's LBD.
1.3 Segment-to-Floor BOM Mapping¶
Each distinct segment in I_Element_Extraction maps to one FLOOR-type m_bom header.
For buildings, a segment is a storey (IfcBuildingStorey). For infrastructure, a segment
is a facility part (IfcBridgePart, IfcRoadPart, IfcRailwayPart). The storey
column in elements_meta stores the segment name regardless of domain.
See InfrastructureAnalysis.md §4 for the hierarchy mapping.
SH (Ifc4_SampleHouse):
| Storey | Floor BOM ID | bom_category | Elements |
|---|---|---|---|
| Ground Floor | SH_GF_STR | GF | 27 |
| Roof | SH_ROOF_STR | RF | 2 |
| Unknown | SH_CW_STR | CW | 26 |
DX (Ifc2x3_Duplex):
| Storey | Floor BOM ID | bom_category | Elements |
|---|---|---|---|
| Level 1 | DX_L1_STR | L1 | 571 |
| Level 2 | DX_L2_STR | L2 | 485 |
| Roof | DX_ROOF_STR | RF | 11 |
| T/FDN | DX_FDN_STR | FN | 7 |
| Unknown | DX_MISC_STR | MS | 25 |
1.4 BUILDING BOM Structure¶
Each extracted building produces one BUILDING-type m_bom with:
| Field | Value | Source |
|---|---|---|
| bom_id | BUILDING_SH_STD / BUILDING_DX_STD | Convention |
| bom_type | — | Root = BOM with no parent m_bom_line. Tier = M_Product_Category. See DISC_VALIDATION_DB_SRS.md §10.4.5 |
| m_product_category_id | RE / CO / IN | Top-level M_Product_Category — classification axis |
| origin_x/y/z | Building world LBD (only BUILDING BOM); (0,0,0) for children — see §4 in BOMBasedCompilation.md | Extraction min corner |
| aabb_width/depth/height_mm | Building envelope | (max - min) × 1000 |
| entity_type | D | Dictionary (read-only) |
World position is derived from M_BOM_Line dx/dy/dz tack offsets (the BOM itself
carries WHERE). {PREFIX}_BOM.db is a pure dictionary — no world coords.
co_empty_space tables were removed S74 (W008) — placement is via M_BOM_Line dx/dy/dz directly.
Children: Each child BOM is linked via a line carrying the tack offset:
dx = position where child's LBD sits within parent (always >= 0, BOMBasedCompilation.md §4).
Child BOMs have origin=(0,0,0) — only BUILDING carries world origin.
Element lines within each FLOOR/DISCIPLINE BOM:
| Field | Source |
|---|---|
| child_product_id | I_Element_Extraction.M_Product_ID |
| role | I_Element_Extraction.ifc_class |
| dx/dy/dz | tack offset (child LBD position within parent, §4) from parent BOM (BOMBasedCompilation.md §4) |
| allocated_width/depth/height_mm | Element AABB × 1000 |
Instance metadata (storey, element_ref, ordinal, orientation, material_name,
material_rgba) is not stored in {PREFIX}_BOM.db dictionary. It remains in
I_Element_Extraction (component_library.db) and is written to output.db at
compile time.
Recipe vs Placement: Each m_bom_line is a type line — one row per unique product type within its parent BOM. For formula-driven elements (TILE, ROUTE, WIRE), a single type line carries qty=N and the compiler expands to N placement instances. For irregular elements, qty=1 (one line = one instance). In a properly factored BOM,
COUNT(m_bom_line)<< element count. SeeBOMBasedCompilation.md§2.1.6 for the full recipe-vs-placement contract.TE factorization DONE (sessions 8-11):
TE_BOM.dbstores 1,131 factored recipe lines (505 products × verb formulas) expanding to 48,428 placement instances at compile time. CLUSTER/TILE/FRAME/ROUTE verbs encode 97.6% of the BOM. SeeTerminalAnalysis.md§BOM Factorization for details.
1.5 Integrity Hash¶
SHA-256 of sorted (bom_id, child_product_id, round(dx,6), round(dy,6), round(dz,6))
for all extracted BOM lines. Stored in ad_sysconfig(config_key='RSTB_INTEGRITY_HASH').
Detects external tampering of m_bom_line data.
1.6 Per-Building BOM Population — Reproducible from Fresh¶
Each building has its own BOM dictionary, regenerated deterministically:
SH / DX (IFCtoBOM Java pipeline — current):
./scripts/run_RosettaStones.sh classify_sh.yaml # → SH_BOM.db
./scripts/run_RosettaStones.sh classify_dx.yaml # → DX_BOM.db
The IFCtoBOM pipeline (IFCtoBOMPipeline.java) — see WorkOrderGuide.md §Step 5 for full table:
1. Load classification YAML (Order input) + read IFC extraction to I_Element_Extraction (ExtractionPopulator)
2. Pre-flight: Auto-discover spatial containers from extraction (YAML override if present); FAIL on NULL M_Product_ID
3. ProductRegistrar.ensureProductCatalog(): M_Product → component_library.db (persistent, reusable)
4. ProductRegistrar.ensureProductImages(): geometry link (INSERT OR IGNORE)
5. Pre-flight: FAIL if any product has no geometry_hash
6. ProductRegistrar.ensureProducts(): copy to BOM DB (transitional for BOMWalker)
7. ScopeBomBuilder / CompositionBomBuilder / StructuralBomBuilder / FloorRoomBomBuilder
8. BomValidator: 9 check methods pre-commit (counts, normalization, offsets, AABB, tack I/O, element refs, product normalization, extraction reconciliation — any FAIL = rollback)
9. IntegrityHash: computes SHA-256 fingerprint (§1.5)
At compile time: run_RosettaStones.sh creates library/_SH_compile.db (or _DX_compile.db) —
a temp copy of {PREFIX}_BOM.db enriched with library/schema_snapshot_bom.sql + C_DocType rows.
Java reads via -Dbom.db=library/_SH_compile.db (System.getProperty("bom.db")) — no hardcoded paths.
Compile DBs are auto-cleaned; only {PREFIX}_BOM.db persists.
Migrations: migration/archive/*.sql — historical only, never executed.
migration/migration_P02_SH_product_link.sql — dead code (replaced by ExtractionPopulator.java).
2. {PREFIX}_BOM.db — Schema Reference¶
Read-only at compile time. Each per-building dictionary contains domain config, BOM hierarchy, and product catalog.
C_DocType (6 rows — domain config)¶
"Construction Order" — one document type. Classification lives on M_Product_Category, not here.
RESOLVED (session 30, R27): IFCtoBOM now writes C_DocType into
{PREFIX}_BOM.dbduring extraction. Shell injection removed fromrun_RosettaStones.sh. DSL content read from YAML-adjacent DSL file. StubDataSeeder workaround retained for unit tests (in-memory DBs have no BOM pipeline).
| Column | Type | Notes |
|---|---|---|
| C_DocType_ID | INTEGER PK | Opaque. Value = RE_SH, RE_DX, CO_TE, etc. See §8 PK convention |
| Name | TEXT | Display name |
| DocBaseType | TEXT | Domain classification (RE/CO/IN/ST) |
| DocSubType | TEXT | Building prefix (SH/DX/TE) |
| ProjectName | TEXT | Building instance name |
| DSLContent | TEXT | DSL template text |
| OutputDbPath | TEXT | Output DB path |
| ReferenceDbPath | TEXT | Reference DB path |
| ExpectedElements | INTEGER | Standard element count |
| Provenance | TEXT | EXTRACTED / GENERATIVE |
| AabbWidthMm, AabbDepthMm, AabbHeightMm | REAL | Standard domain AABB |
| IsActive | INTEGER | Active flag |
m_bom (BOM headers)¶
2 selection dimensions: M_Product_Category (substitution shelf) + AD_Org (discipline). AABB is spatial qualification only — envelope fit, not selection.
| Column | Type | Notes |
|---|---|---|
| M_BOM_ID | INTEGER PK | Opaque. Value = BUILDING_SH_STD, SH_GF_STR, BED_SET. See §8 PK convention |
| bom_name | TEXT | Display name |
| bom_type | TEXT | Legacy tier label. Root is identified by having no parent m_bom_line. Tier selection uses M_Product_Category. See DISC_VALIDATION_DB_SRS.md §6.5 |
| bom_category | TEXT | Functional role — FK → M_Product_Category |
| m_product_category_id | INTEGER FK | Domain classification (RE/CO/IN) — see M_Product_Category |
| origin_x, origin_y, origin_z | REAL | BUILDING BOM: world LBD; all others: (0,0,0). See BOMBasedCompilation.md §4. |
| aabb_width_mm, aabb_depth_mm, aabb_height_mm | INTEGER | Envelope dimensions |
| group_by | TEXT | Grouping key |
| entity_type | TEXT | D=Dictionary, U=User, A=Application |
| is_active | INTEGER |
m_bom_line (BOM children — recipe type lines, NOT instance placements)¶
Each row is a type line: one unique product within its parent BOM. The compiler
expands type lines into placement instances via verb formulas (TILE, ROUTE, FRAME)
or flat qty. Instance-level data (per-element coordinates) belongs in output.db.
The walker decides BOM-vs-leaf by whether child_product_id resolves to an
m_bom row — component_type plays no role (BOMBasedCompilation.md §2.2.1).
| Column | Type | Notes |
|---|---|---|
| bom_child_id | INTEGER PK | Auto-increment |
| bom_id | TEXT FK | → m_bom.bom_id |
| child_product_id | TEXT | → M_Product or m_bom.bom_id (walker decides by existence) |
| component_type | TEXT | iDempiere compat only — compilation ignores this column |
| role | TEXT | IFC class (extracted) or functional role |
| dx, dy, dz | REAL | tack offset (child LBD position within parent, §4) from parent BOM (metres, BOMBasedCompilation.md §4) |
| allocated_width_mm, allocated_depth_mm, allocated_height_mm | INTEGER | Per-instance AABB (mm) |
| storey | TEXT | NULL in {PREFIX}_BOM.db dictionary — output.db only (from I_Element_Extraction) |
| element_ref | TEXT | NULL in {PREFIX}_BOM.db dictionary — output.db only (from I_Element_Extraction) |
| ordinal | INTEGER | NULL in {PREFIX}_BOM.db dictionary — output.db only (from I_Element_Extraction) |
| orientation | TEXT | NULL in {PREFIX}_BOM.db dictionary — output.db only (from I_Element_Extraction) |
| material_name, material_rgba | TEXT | NULL in {PREFIX}_BOM.db dictionary — material lives on M_Product |
| entity_type | TEXT | D=Dictionary |
| sequence | INTEGER | Sort order |
| is_active | INTEGER | |
| rotation_rule | TEXT | Rotation encoding |
| fit_priority | INTEGER | Tiebreaker (default=20) |
M_Product (transitional copy — master in component_library.db §3)¶
Each distinct M_Product_ID in I_Element_Extraction becomes an M_Product row. BOM assembly stubs (MAKE references) get sentinel dims (0.001). This is a transitional copy. The master catalog lives in component_library.db (see §3 M_Product). Target: BOMWalker reads from component_library.db directly.
| Column | Type | Notes |
|---|---|---|
| M_Product_ID | INTEGER PK | Opaque. Value = product identifier (e.g. SH_WALL_001). See §8 |
| product_type | TEXT | DOOR/WALL/FURNITURE/SET/FLOOR/etc. |
| width, depth, height | REAL | Dimensions (metres) |
| ifc_class | TEXT | IFC entity type |
| extracted_from | TEXT | Source building |
| material_name, material_rgba | TEXT | Catalog material |
| M_Product_Category_ID | INTEGER FK | → M_Product_Category. See §8 PK convention |
| cost_uom | TEXT | Trade UOM — see table below. Drives 5D cost (qty × unit_cost). Future: C_UOM_ID INTEGER FK |
| is_active | INTEGER |
cost_uom values (trade unit of measure per product type):
| UOM | Meaning | Used For | Standard |
|---|---|---|---|
| EA | Each (discrete item) | Fittings, terminals, valves, doors, furniture, alarms | — |
| M | Linear meter | Pipe segments, duct segments, beams, columns, rails | — |
| M2 | Square meter | Walls, slabs, roofs, coverings, masonry courses | — |
| M3 | Cubic meter | Concrete (footings, earthworks fill) only | — |
| KG | Kilogram | Reinforcing bar (rebar) | PWD 203A, NRM2, AIQS |
See DISC_VALIDATION_DB_SRS §10.4.11 T3.5 for the DV029 migration that corrects MEP/rebar/covering UOMs from extraction defaults.
M_Product_Category (flat classification — iDempiere standard)¶
M_Product_Category is FLAT. No parent FK. No tree. This follows iDempiere standard
where M_Product_Category is a simple grouping tag. The building→floor→room→leaf cascade
is expressed by the BOM tree (M_BOM → M_BOM_Line parent-child relationships), not by
the category table. To enforce swap-pool constraints, match m_product_category_id at
the same BOM level — you never need to traverse a category tree.
Note:
Parent_Category_IDis legacy — the BOM cascade already expresses hierarchy through M_BOM_Line parent-child relationships.
117 rows total (DV018 migration). Representative categories:
| Level | M_Product_Category_ID | Name | Example Buildings |
|---|---|---|---|
| Top-level | RE, CO, IN, IP | Residential, Commercial, Infrastructure, Industrial Plant | SH, DX, TE, BR |
| Floor | GF, L1, L2, L3, RF, FN, MS | Ground Floor, Level 1–3, Roof, Foundation, Misc | All buildings |
| Room (RE) | LIVING, KITCHEN, BEDROOM, BATHROOM, DINING, MASTER, CORRIDOR | Room types — swap pool at room level | SH, DM, DX, FK, AC |
| Discipline (CO) | ARC, STR, FP, ELEC, ACMV, CW, SP, LPG | Discipline types — swap pool at discipline level | TE, WA, WL, WT |
| Infra segment | SUP, DCK, ABT, TRK, ROAD, RAIL, GEO | Segment types | BR, RD, RL |
| Structural | PAIR, HALF_UNIT, SLAB, PORCH | Assembly containers | DX |
Full list: migration/DV018_product_category_cascade.sql
3. component_library.db — Product LOD Catalog + Geometry¶
Source of truth for product definitions, geometry, orientation, and extraction archive.
M_Product is the persistent product catalog (created by ProductRegistrar.ensureProductCatalog(),
INSERT OR IGNORE = reused across buildings). M_Product_Image links products to geometry.
See WorkOrderGuide.md §"Drift Prevention" for enforced guards.
Discipline metadata (ad_space_type, ad_element_mep, ad_wall_face, placement_rules, etc.) lives in ERP.db — see DISC_VALIDATION_DB_SRS.md.
I_Element_Extraction (IFC extraction archive)¶
The source of truth for extracted building element positions.
Read by the IFCtoBOM Java pipeline (SH/DX) or RosettaStoneToBOM.py (TE legacy) to generate m_bom + m_bom_line.
| Column | Type | Notes |
|---|---|---|
| placement_id | INTEGER PK | |
| building_type | TEXT | Ifc4_SampleHouse / Ifc2x3_Duplex |
| storey | TEXT | Storey name |
| ifc_class | TEXT | IFC entity type |
| element_ref | TEXT | Element reference |
| ordinal | INTEGER | Position order |
| min_x, max_x, min_y, max_y, min_z, max_z | REAL | World-space AABB |
| orientation | TEXT | NS/EW/POINT |
| material_name, material_rgba | TEXT | Material |
| M_Product_ID | TEXT | FK → M_Product (same DB — master catalog) |
M_Product (persistent product catalog)¶
Master product catalog. Created by ProductRegistrar.ensureProductCatalog() from
extraction data. INSERT OR IGNORE = products reused across buildings. Transitionally
copied to {PREFIX}_BOM.db for BOMWalker compatibility.
| Column | Type | Notes |
|---|---|---|
| M_Product_ID | INTEGER PK | Value = element_ref (deterministic). See §8 |
| product_type | TEXT | Derived from IFC class (WALL, DOOR, etc.) |
| width, depth, height | REAL | Intrinsic dimensions in metres |
| ifc_class | TEXT | Source IFC entity type |
| extracted_from | TEXT | Always 'IFC_EXTRACTION' |
| building_type | TEXT | Source building |
M_Product_Image (product → geometry mapping)¶
Auto-created by ProductRegistrar.ensureProductImages() — deterministic join of
I_Element_Extraction × I_Geometry_Map. INSERT OR IGNORE. Never manually migrated.
| Column | Type | Notes |
|---|---|---|
| M_Product_ID | TEXT | FK → M_Product (same DB) |
| geometry_hash | TEXT | FK → LOD_Object |
LOD_Object (canonical meshes)¶
| Column | Type | Notes |
|---|---|---|
| geometry_hash | TEXT PK | SHA-256 of geometry |
| vertices | BLOB | Vertex data |
| faces | BLOB | Face indices |
| vertex_count, face_count | INTEGER |
4. output.db — Transactional (Fresh Each Compile)¶
Created by CompilationPipeline. C_Order created from C_DocType at compile time.
Coordinate Flow (compilation)¶
{PREFIX}_BOM.db component_library.db
m_bom.origin_x/y/z ──┐ M_Product_Image ──→ MeshBinder
m_bom_line.dx/dy/dz ──┤
m_bom_line.allocated ──┤
↓
PlacementCollectorVisitor
anchor = origin + Σ(line.dx + child.origin)
cx = anchor + dx
minX = cx - halfW
↓
output.db
elements_meta + elements_rtree (world AABB)
c_orderline (WHAT)
(co_empty_space removed S74 — WHERE = M_BOM_Line dx/dy/dz)
W_Verb_Node (HOW)
Key Tables¶
| Table | Purpose |
|---|---|
| c_order | Building instance (from C_DocType) |
| c_orderline | Element instances (from BOM explosion) |
| elements_meta | Element metadata (guid, ifc_class, storey) |
| elements_rtree | Spatial index (world AABB) |
| ~~co_empty_space~~ | (removed S74 — W008) |
| ~~co_empty_space_line~~ | (removed S74 — W008) |
| W_Verb_Node | Verb execution audit trail |
| base_geometries | Geometry meshes (copied from component_library) |
| element_instances | Element → geometry mapping |
5. Cross-DB FK Map¶
| output.db Column | References | Target DB |
|---|---|---|
| c_order.C_DocType_ID | C_DocType.C_DocType_ID | {PREFIX}_BOM.db |
| c_orderline.M_Product_ID | M_Product.product_id | {PREFIX}_BOM.db (transitional copy; master in component_library.db) |
| element_instances.geometry_hash | LOD_Object.geometry_hash | component_library.db |
| ~~co_empty_space_line.bom_id~~ | (removed S74 — W008) | — |
6. AD Data Placement — The 4-DB Architecture¶
6.1 Current State — Where AD Tables Live¶
| Table | ERP.db | component_library.db | {PREFIX}_BOM.db | Purpose |
|---|---|---|---|---|
| AD_Org | 16 rows | — | — | Discipline definitions (ARC, STR, FP...) |
| M_Product_Category | IFC→discipline map | IFC→discipline map | 0 rows (DM only) | Product classification |
| AD_SysConfig | 8 rows (schema versions) | — | 2 rows (integrity hash, expected elements) | Different schema, different purpose |
| ad_val_rule + param (mined) | 415 + 1,245 rows | — | — | Mined dimension ranges |
| AD_Val_Rule + Param (compliance) | 63 + authored | — | — | Compliance rules (merged into ERP.db) |
| C_DocType | — | — | 1 row per building | Building type classification |
| M_Product | 2,477 rows | 2,475 rows (master) | 2–93 rows (transitional copy) | Product catalog |
| M_Attribute* | — | — | per-building | Instance customization |
| m_bom + m_bom_line | — | — | per-building | Spatial BOM (the core per-building data) |
6.2 Duplication Analysis¶
M_Product is the most duplicated table:
- component_library.db is the master catalog (created by ProductRegistrar.ensureProductCatalog())
- ERP.db has a near-identical copy (2,477 vs 2,475) used for validation queries
- {PREFIX}_BOM.db has small transitional copies for BOMWalker compatibility (target: remove)
M_Product_Category exists in both ERP.db and component_library.db with identical IFC class mappings. Only one authoritative copy is needed.
AD_SysConfig is NOT truly duplicated — different schemas serve different purposes:
- ERP.db: Name/Value pairs tracking schema migration versions (DV001–DV015)
- BOM databases: config_key/config_value pairs tracking per-building integrity hashes
C_DocType lives only in BOM databases (1 row each, e.g., RE_SH, RE_DX). In iDempiere,
C_DocType is shared system configuration — it defines all document types centrally. Currently
written by IFCtoBOMPipeline per-building; at compile time, schema_snapshot_bom.sql enriches
the compile DB with additional C_DocType rows.
6.3 The 4-DB Split¶
In iDempiere, AD tables (Application Dictionary) are shared across all organisations. They live once, centrally — not per-product or per-order. ERP.db serves this role (renamed from disc_validation.db in S76; validation.db merged in — all compliance rules now live in ERP.db alongside mined dimension rules).
Note: ERP.db contains two sets of ad_val_rule / ad_val_rule_param rows with
different provenance — mined dimension ranges (415 rules) and authored compliance
rules (63 rules). Same table, different provenance. Distinguished by source column.
| Layer | Database | Contains |
|---|---|---|
| ERP shared | ERP.db |
AD_Org, M_Product_Category, C_DocType (master), AD_SysConfig (schema versions), ad_val_rule (mined + compliance), AD_Clash_Rule, AD_Occupancy_Class, all ad_* discipline metadata |
| Product catalog | component_library.db |
M_Product (master), M_Product_Image, LOD_Object, I_Element_Extraction, geometry |
| Per-building BOM | {PREFIX}_BOM.db |
m_bom, m_bom_line, M_Attribute*, ad_sysconfig (per-building integrity), C_DocType (compile-time copy) |
| Transactional | output.db |
c_order, c_orderline, elements_meta, W_Verb_Node |
Remaining consolidation targets: - M_Product_Category: single authoritative copy in ERP.db (currently also in component_library.db) - M_Product in ERP.db: evaluate whether validation queries can read from component_library.db directly - BOM databases keep transitional M_Product copies until BOMWalker migration completes
See MANIFESTO.md §Three Concerns for the WHAT/HOW/WHERE rationale behind this split.
7. DocBaseType → M_Product_Category Migration¶
Status: Steps 1–7 DONE. Classification lives on m_product_category_id at every BOM level.
doc_base_type removed (S84, W012). doc_sub_type removed — prefix on bom_id carries this.
AD_Org_ID replaces bom_category strings for discipline routing. C_DocType = ONE "Construction Order".
7.1 What Was Done¶
- S75: M_Product_Category DV018 migration (117 rows), BUILDING BOM backfill
- S76: disc_validation.db renamed to ERP.db (~100 files)
- S77: Java routing migrated to m_product_category_id (19 source + 12 test files)
- S78: AD_Org_ID FK on m_bom, C_OrderLine (W009 migration)
- S79: Discipline enum replaces deriveDiscipline() in compile path
8. iDempiere PK Convention¶
Every master table follows the iDempiere TableName_ID / Value / Name convention:
TableName_ID—INTEGER PRIMARY KEY AUTOINCREMENT. Opaque surrogate key. Never shown to users, never used in business logic.Value—TEXT NOT NULL UNIQUE. The search key (what was previously the TEXT PK). Lookups by business key useWHERE Value = ?.Name—TEXT NOT NULL. Display name for UI and reports.- Foreign keys always reference
_ID, neverValue. Joins are integer comparisons.
This is standard iDempiere convention: every X_ generated class has getTableName_ID(), getValue(), getName(). The migration from TEXT PKs to INTEGER PKs preserves the old TEXT identifier as Value.
8.1 Migration Status¶
| Table | Status | Since |
|---|---|---|
| M_Product | INTEGER PK | S91 |
| C_Order | INTEGER PK | S91 |
| C_DocType | INTEGER PK | S91 |
| AD_Org | INTEGER PK (manual) | pre-S90 |
| ad_val_rule | INTEGER PK | S90 |
| M_BOM (ERP.db) | INTEGER PK | DV025 |
| m_bom (BOM.db) | INTEGER PK | S100-p86 Phase A |
| M_Product_Category | INTEGER PK | Phase B |
| 13 AD tables | INTEGER PK | Phase C |
8.2 Convention Rules¶
- New tables must use
TableName_ID INTEGER PRIMARY KEY AUTOINCREMENTfrom creation. No TEXT PKs for new tables. - Existing tables migrated in phases: Phase A (m_bom), Phase B (M_Product_Category), Phase C (remaining AD tables).
_intsidecar columns (transitional pattern from S90-S92) are dropped as each table completes migration. Phase D dropped M_Product_Category_ID_int (S92).loadByValue(String)on BasePO enablesWHERE Value = ?lookups — replaces the old TEXT PK pattern without changing caller semantics.
AD_Org unifies discipline identifiers from TEXT strings to integer FK. See DISC_VALIDATION_DB_SRS.md §6.
Authoritative reference. See BOMBasedCompilation.md for tack convention and pipeline stages,
MANIFESTO.md for the three-concern model (WHAT/HOW/WHERE),
TheRosettaStoneStrategy.md for verification strategy.