mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 14:46:55 +01:00
Skaerp (Edison): erkend naar Setup-vs-Subsidiary ikke kan afgoeres fra kilden alene
This commit is contained in:
parent
db3380711b
commit
d2b7d834d0
1 changed files with 119 additions and 118 deletions
|
|
@ -1,118 +1,119 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: architecture
|
||||
keywords: [tables, table-design, naming-conventions, primary-key, master-table, ledger-table, journal-table, register-table, document-table, setup-table, subsidiary-table, supplemental-table]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# CURABIS Architecture: BC Table-Type Conventions
|
||||
|
||||
## Description
|
||||
|
||||
Business Central's Base Application follows nine recurring table types — Master,
|
||||
Supplemental, Subsidiary, Ledger, Register, Journal, Document, Document History,
|
||||
and Setup. Each type fixes a naming pattern, a primary-key shape, and a set of
|
||||
associated pages. A new or extended table whose design doesn't match the
|
||||
conventions of its own type is either misclassified or built inconsistently
|
||||
with the rest of the application, and should be flagged in review even if it
|
||||
compiles.
|
||||
|
||||
## Key Principle
|
||||
|
||||
"Before assigning a primary key or naming a new table, first ask: which of the
|
||||
nine standard table types is this? The answer fixes almost every other design
|
||||
decision — key shape, naming suffix, and which pages must exist."
|
||||
|
||||
## The Nine Table Types
|
||||
|
||||
### 1. Master
|
||||
- **Purpose:** primary focus subject of a functional area (Customer, Vendor, Item).
|
||||
- **Naming:** name of one record in the table, singular (`Customer`, `Item`).
|
||||
- **Primary key:** `Code[20]` named `No.` (occasionally `Code`).
|
||||
- **Description field:** `Text[80]` named `Name` or `Description`, included in `DataCaptionFields`.
|
||||
- **Pages:** Card (edit), List (view/lookup/drilldown — `LookupPageID`/`DrillDownPageID`), Statistics (calculated info, kept separate for performance).
|
||||
|
||||
### 2. Supplemental
|
||||
- **Purpose:** subject used across one or more functional areas (Currency, Language) — not the primary focus of any single area.
|
||||
- **Naming:** name of one record (`Currency`).
|
||||
- **Primary key:** `Code[10]` named `Code`.
|
||||
- **Description field:** `Text[50]` named `Description` (some have none, some use `Name`).
|
||||
- **Pages:** one List page, plural of the table name (`Currencies`), set as `LookupPageID`.
|
||||
|
||||
### 3. Subsidiary
|
||||
- **Purpose:** information subsidiary to a Master and/or Supplemental table (Item Vendor, FA Depreciation Book).
|
||||
- **Naming:** built from the table(s) it's subsidiary to (`Item Vendor`).
|
||||
- **Primary key:** one field per table it's subsidiary to, each related to that table, optionally an `Integer` `Line No.` at the end to disambiguate. No description field.
|
||||
- **Pages:** Worksheet page (PK contains an Integer → PK fields excluded/auto-filtered) or Tabular page (PK has no Integer); always linked back to and filtered by the calling page.
|
||||
|
||||
### 4. Ledger
|
||||
- **Purpose:** transactional information that is the primary focus of a functional area (Cust. Ledger Entry, Item Ledger Entry).
|
||||
- **Naming:** related Master table name + `Ledger Entry`.
|
||||
- **Primary key:** `Integer` named `Entry No.`, always auto-generated by the posting routine — never user-editable; no add/delete except tightly controlled exceptions.
|
||||
- **Secondary keys:** typically carry `SumIndexFields`; at least one has the Master-table-relation field first, paired with FlowFields on the Master table.
|
||||
- **Pages:** List page, plural of table name (`Customer Ledger Entries`), set as `LookupPageID`/`DrillDownPageID`.
|
||||
|
||||
### 5. Register
|
||||
- **Purpose:** table of contents for its corresponding Ledger table(s), one record per posting process (G/L Register).
|
||||
- **Naming:** posting function name + `Register`.
|
||||
- **Primary key:** `Integer` named `No.`, auto-generated by the posting routine, never user-editable.
|
||||
- **Other standard fields:** `From Entry No.` and `To Entry No.` (Integer, related to the Ledger table).
|
||||
- **Pages:** List page, plural of table name, with a `Register` menu button linking to the Ledger List page(s).
|
||||
|
||||
### 6. Journal
|
||||
- **Purpose:** primary transactional table where users (or another posting routine) enter data before it is posted to a Ledger table.
|
||||
- **Naming:** transaction type + `Journal Line` (`Resource Journal Line`).
|
||||
- **Primary key:** Journal Template field + Journal Batch field + `Integer` `Line No.`.
|
||||
- **Related Supplemental tables:** `<X> Journal Template`, `<X> Journal Batch`.
|
||||
- **Pages:** Worksheet page named like the Journal table minus `Line`, filtered by Template/Batch, `AutoSplitKey` sets `Line No.`; includes a `Posting` menu button and a CTRL+F7 link to the related Ledger.
|
||||
|
||||
### 7. Document (Header + Line)
|
||||
- **Purpose:** secondary transactional tables — post to Ledgers through Journal tables, not directly (Sales Header/Line).
|
||||
- **Naming:** transaction/document name + `Header` or `Line`.
|
||||
- **Primary key (Header):** `Code[20]` `No.`, or — if the table holds multiple document kinds — `Option` `Document Type` + `Code[20]` `No.`.
|
||||
- **Primary key (Line):** the Header's key field(s), renamed to `<DocumentName> No.` (without "Header"), + `Integer` `Line No.`.
|
||||
- **Pages:** Header uses a Document/Card page with a `Posting` action and a subpage control containing the Document Lines page; Line uses a Worksheet/ListPart page, PK fields hidden via `AutoSplitKey`/link filtering.
|
||||
|
||||
### 8. Document History
|
||||
- **Purpose:** posted copy of a Document table, populated during posting (Sales Invoice Header/Line).
|
||||
- **Naming:** same as the source Document table, with `Posted` or `Issued` inserted.
|
||||
- **Primary key / fields:** mirrors the source Document table's field numbers, names, and properties.
|
||||
- **Editability:** never user-editable; deletable only with explicit permission.
|
||||
- **Pages:** same shape as the Document pages, but the Line-equivalent uses a List page (no editing) instead of a Worksheet page.
|
||||
|
||||
### 9. Setup
|
||||
- **Purpose:** exactly one record holding options/settings for a functional area or the company as a whole (General Ledger Setup).
|
||||
- **Naming:** functional area name + `Setup` (exception: `Company Information`).
|
||||
- **Primary key:** `Code[10]` named `Primary Key`, always left blank — enforces the single-record rule.
|
||||
- **Pages:** one page, same name as the table, primary key field not shown.
|
||||
- **Record instantiation:** the page's `OnOpenPage` trigger — not the table — creates the singleton the first time it is opened; it is never assumed to pre-exist. Standard shape: `Rec.Reset(); if not Rec.Get() then begin Rec.Init(); Rec.Insert(); end;` (the `Reset()` clears any stale filter before the `Get()`, since the blank `Code` PK would otherwise be vulnerable to one). Verified against Base App W1: `General Ledger Setup` and `Sales & Receivables Setup` both use this exact pattern. A Setup page that omits this and assumes the record exists fails at runtime on first open in a fresh company.
|
||||
- **Caveat:** a table with "Setup" in its name that holds more than one record follows the Subsidiary-table rules instead — the name alone is not proof of type.
|
||||
|
||||
## Review Checklist
|
||||
|
||||
When reviewing or designing a table, ask:
|
||||
1. Which of the 9 types is this actually — based on its role, not its name?
|
||||
2. Does the primary key shape match that type (Code+`No.` / Integer+`Entry No.` / Code+`Line No.` / blank Code+`Primary Key` / etc.)?
|
||||
3. Does the table name carry the expected suffix (`Ledger Entry`, `Journal Line`, `Register`, `Setup`, `Header`/`Line`)?
|
||||
4. Do the expected pages exist (Card+List(+Statistics) for Master, Worksheet for Journal/Subsidiary-with-Integer-PK, List for Ledger/Register/Document History, Document/Card+subpage for Document Header)?
|
||||
5. If a Ledger/Register/Document History table's primary key is user-editable, or a Master/Setup table allows duplicate identity, flag it — the design contradicts its own type.
|
||||
6. For a Setup table's page, does `OnOpenPage` create the singleton record (`Get` → `Init` → `Insert`, guarded by `Reset`) instead of assuming it already exists?
|
||||
|
||||
A table that mixes conventions from two types (for example, a "Ledger" table that
|
||||
lets users freely insert or delete rows) is not "flexible" — it is either
|
||||
misclassified or has skipped a design step.
|
||||
|
||||
## Source
|
||||
|
||||
CURABIS Academy course "Your Key to Application Language for Microsoft Business
|
||||
Central" (rev. July 2022), Chapter 2: Tables, "Table Types and Characteristics"
|
||||
(p. 73–84). Cross-checked against the current Base Application table structure
|
||||
as of 2026-08-12 — the taxonomy still holds.
|
||||
|
||||
Setup-page record-instantiation pattern (2026-08-13) verified directly against
|
||||
`microsoft/BCApps` source (W1 layer): `src/Layers/W1/BaseApp/Finance/GeneralLedger/Setup/GeneralLedgerSetup.Page.al`
|
||||
(`OnOpenPage`, line ~889) and `src/Layers/W1/BaseApp/Sales/Setup/SalesReceivablesSetup.Page.al`
|
||||
(`OnOpenPage`, line ~601) — not inferred from the Academy material, which does
|
||||
not document this detail.
|
||||
---
|
||||
bc-version: [all]
|
||||
domain: architecture
|
||||
keywords: [tables, table-design, naming-conventions, primary-key, master-table, ledger-table, journal-table, register-table, document-table, setup-table, subsidiary-table, supplemental-table]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# CURABIS Architecture: BC Table-Type Conventions
|
||||
|
||||
## Description
|
||||
|
||||
Business Central's Base Application follows nine recurring table types — Master,
|
||||
Supplemental, Subsidiary, Ledger, Register, Journal, Document, Document History,
|
||||
and Setup. Each type fixes a naming pattern, a primary-key shape, and a set of
|
||||
associated pages. A new or extended table whose design doesn't match the
|
||||
conventions of its own type is either misclassified or built inconsistently
|
||||
with the rest of the application, and should be flagged in review even if it
|
||||
compiles.
|
||||
|
||||
## Key Principle
|
||||
|
||||
"Before assigning a primary key or naming a new table, first ask: which of the
|
||||
nine standard table types is this? The answer fixes almost every other design
|
||||
decision — key shape, naming suffix, and which pages must exist."
|
||||
|
||||
## The Nine Table Types
|
||||
|
||||
### 1. Master
|
||||
- **Purpose:** primary focus subject of a functional area (Customer, Vendor, Item).
|
||||
- **Naming:** name of one record in the table, singular (`Customer`, `Item`).
|
||||
- **Primary key:** `Code[20]` named `No.` (occasionally `Code`).
|
||||
- **Description field:** `Text[80]` named `Name` or `Description`, included in `DataCaptionFields`.
|
||||
- **Pages:** Card (edit), List (view/lookup/drilldown — `LookupPageID`/`DrillDownPageID`), Statistics (calculated info, kept separate for performance).
|
||||
|
||||
### 2. Supplemental
|
||||
- **Purpose:** subject used across one or more functional areas (Currency, Language) — not the primary focus of any single area.
|
||||
- **Naming:** name of one record (`Currency`).
|
||||
- **Primary key:** `Code[10]` named `Code`.
|
||||
- **Description field:** `Text[50]` named `Description` (some have none, some use `Name`).
|
||||
- **Pages:** one List page, plural of the table name (`Currencies`), set as `LookupPageID`.
|
||||
|
||||
### 3. Subsidiary
|
||||
- **Purpose:** information subsidiary to a Master and/or Supplemental table (Item Vendor, FA Depreciation Book).
|
||||
- **Naming:** built from the table(s) it's subsidiary to (`Item Vendor`).
|
||||
- **Primary key:** one field per table it's subsidiary to, each related to that table, optionally an `Integer` `Line No.` at the end to disambiguate. No description field.
|
||||
- **Pages:** Worksheet page (PK contains an Integer → PK fields excluded/auto-filtered) or Tabular page (PK has no Integer); always linked back to and filtered by the calling page.
|
||||
|
||||
### 4. Ledger
|
||||
- **Purpose:** transactional information that is the primary focus of a functional area (Cust. Ledger Entry, Item Ledger Entry).
|
||||
- **Naming:** related Master table name + `Ledger Entry`.
|
||||
- **Primary key:** `Integer` named `Entry No.`, always auto-generated by the posting routine — never user-editable; no add/delete except tightly controlled exceptions.
|
||||
- **Secondary keys:** typically carry `SumIndexFields`; at least one has the Master-table-relation field first, paired with FlowFields on the Master table.
|
||||
- **Pages:** List page, plural of table name (`Customer Ledger Entries`), set as `LookupPageID`/`DrillDownPageID`.
|
||||
|
||||
### 5. Register
|
||||
- **Purpose:** table of contents for its corresponding Ledger table(s), one record per posting process (G/L Register).
|
||||
- **Naming:** posting function name + `Register`.
|
||||
- **Primary key:** `Integer` named `No.`, auto-generated by the posting routine, never user-editable.
|
||||
- **Other standard fields:** `From Entry No.` and `To Entry No.` (Integer, related to the Ledger table).
|
||||
- **Pages:** List page, plural of table name, with a `Register` menu button linking to the Ledger List page(s).
|
||||
|
||||
### 6. Journal
|
||||
- **Purpose:** primary transactional table where users (or another posting routine) enter data before it is posted to a Ledger table.
|
||||
- **Naming:** transaction type + `Journal Line` (`Resource Journal Line`).
|
||||
- **Primary key:** Journal Template field + Journal Batch field + `Integer` `Line No.`.
|
||||
- **Related Supplemental tables:** `<X> Journal Template`, `<X> Journal Batch`.
|
||||
- **Pages:** Worksheet page named like the Journal table minus `Line`, filtered by Template/Batch, `AutoSplitKey` sets `Line No.`; includes a `Posting` menu button and a CTRL+F7 link to the related Ledger.
|
||||
|
||||
### 7. Document (Header + Line)
|
||||
- **Purpose:** secondary transactional tables — post to Ledgers through Journal tables, not directly (Sales Header/Line).
|
||||
- **Naming:** transaction/document name + `Header` or `Line`.
|
||||
- **Primary key (Header):** `Code[20]` `No.`, or — if the table holds multiple document kinds — `Option` `Document Type` + `Code[20]` `No.`.
|
||||
- **Primary key (Line):** the Header's key field(s), renamed to `<DocumentName> No.` (without "Header"), + `Integer` `Line No.`.
|
||||
- **Pages:** Header uses a Document/Card page with a `Posting` action and a subpage control containing the Document Lines page; Line uses a Worksheet/ListPart page, PK fields hidden via `AutoSplitKey`/link filtering.
|
||||
|
||||
### 8. Document History
|
||||
- **Purpose:** posted copy of a Document table, populated during posting (Sales Invoice Header/Line).
|
||||
- **Naming:** same as the source Document table, with `Posted` or `Issued` inserted.
|
||||
- **Primary key / fields:** mirrors the source Document table's field numbers, names, and properties.
|
||||
- **Editability:** never user-editable; deletable only with explicit permission.
|
||||
- **Pages:** same shape as the Document pages, but the Line-equivalent uses a List page (no editing) instead of a Worksheet page.
|
||||
|
||||
### 9. Setup
|
||||
- **Purpose:** exactly one record holding options/settings for a functional area or the company as a whole (General Ledger Setup).
|
||||
- **Naming:** functional area name + `Setup` (exception: `Company Information`).
|
||||
- **Primary key:** `Code[10]` named `Primary Key`, always left blank — enforces the single-record rule.
|
||||
- **Pages:** one page, same name as the table, primary key field not shown.
|
||||
- **Record instantiation:** the page's `OnOpenPage` trigger — not the table — creates the singleton the first time it is opened; it is never assumed to pre-exist. Standard shape: `Rec.Reset(); if not Rec.Get() then begin Rec.Init(); Rec.Insert(); end;` (the `Reset()` clears any stale filter before the `Get()`, since the blank `Code` PK would otherwise be vulnerable to one). Verified against Base App W1: `General Ledger Setup` and `Sales & Receivables Setup` both use this exact pattern. A Setup page that omits this and assumes the record exists fails at runtime on first open in a fresh company.
|
||||
- **Caveat:** a table with "Setup" in its name that holds more than one record follows the Subsidiary-table rules instead — the name alone is not proof of type.
|
||||
- **When the object definition alone can't resolve the caveat:** a "Setup"-named table with a real business-field primary key (not a blank `Code[10]` "Primary Key") and no associated page looks like a violation of both the Setup and Subsidiary shapes at once. Confirming which one it actually is requires checking real row cardinality (does the table ever hold more than one record in practice?) or tracing the table's other call sites — not something the table/page definitions alone settle. When source can't resolve it, say so explicitly rather than forcing a classification (Edison eval 2026-08-13, Wareco @ a2fc8ff8, `ForsendelsesSetup.Table.al`).
|
||||
|
||||
## Review Checklist
|
||||
|
||||
When reviewing or designing a table, ask:
|
||||
1. Which of the 9 types is this actually — based on its role, not its name?
|
||||
2. Does the primary key shape match that type (Code+`No.` / Integer+`Entry No.` / Code+`Line No.` / blank Code+`Primary Key` / etc.)?
|
||||
3. Does the table name carry the expected suffix (`Ledger Entry`, `Journal Line`, `Register`, `Setup`, `Header`/`Line`)?
|
||||
4. Do the expected pages exist (Card+List(+Statistics) for Master, Worksheet for Journal/Subsidiary-with-Integer-PK, List for Ledger/Register/Document History, Document/Card+subpage for Document Header)?
|
||||
5. If a Ledger/Register/Document History table's primary key is user-editable, or a Master/Setup table allows duplicate identity, flag it — the design contradicts its own type.
|
||||
6. For a Setup table's page, does `OnOpenPage` create the singleton record (`Get` → `Init` → `Insert`, guarded by `Reset`) instead of assuming it already exists?
|
||||
|
||||
A table that mixes conventions from two types (for example, a "Ledger" table that
|
||||
lets users freely insert or delete rows) is not "flexible" — it is either
|
||||
misclassified or has skipped a design step.
|
||||
|
||||
## Source
|
||||
|
||||
CURABIS Academy course "Your Key to Application Language for Microsoft Business
|
||||
Central" (rev. July 2022), Chapter 2: Tables, "Table Types and Characteristics"
|
||||
(p. 73–84). Cross-checked against the current Base Application table structure
|
||||
as of 2026-08-12 — the taxonomy still holds.
|
||||
|
||||
Setup-page record-instantiation pattern (2026-08-13) verified directly against
|
||||
`microsoft/BCApps` source (W1 layer): `src/Layers/W1/BaseApp/Finance/GeneralLedger/Setup/GeneralLedgerSetup.Page.al`
|
||||
(`OnOpenPage`, line ~889) and `src/Layers/W1/BaseApp/Sales/Setup/SalesReceivablesSetup.Page.al`
|
||||
(`OnOpenPage`, line ~601) — not inferred from the Academy material, which does
|
||||
not document this detail.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue