Supply-Org Pricing
The correct pricing grain is
(material, supply_org)— notmaterialalone.
The Question
Section titled “The Question”When a material is delivered to a cost centre, which catalogue entry holds the correct price?
The traditional answer is a rule: “pick the latest creation date”, “pick the highest active price”, “pick the most recent actif=oui record”. These rules are proxies. They sometimes get it right and sometimes don’t — and when they’re wrong, they’re wrong in a way that’s hard to detect: there’s no complaint from the query, just a number that silently disagrees with the hospital’s own invoice.
The empirical discovery, traced through a painful SZO 2024 reconciliation:
The correct price is always the one on the catalogue entry whose fulfilling organization matches the fulfilling organization of the delivery.
When the fulfilling organization delivers an article, a billing flow downstream catches errors (like price = 0) and corrects them at the fulfilling organization’s catalogue entry. The other organization’s entry — which did not deliver the article — stays uncorrected and becomes stale. The fulfilling organization’s price is always the audited one, even when its record is marked inactive.
This Is Not a Bug in the Rules
Section titled “This Is Not a Bug in the Rules”Our existing rules (if_only_one_active_then_this_else_latest, highest_price, lowest_price, latest_valid) all operate on a collapsed grain: given a material, pick one price. They do this because gold_price_series was keyed by (material_id, catalogue_id, effective_from) — not by (material_id, supply_org, effective_from).
This collapse is the bug. There is no combination of rules that can reconstruct a missing dimension. The fix is not a better rule; it is a better grain.
What a Supply-Org Is
Section titled “What a Supply-Org Is”A supply_org is the smallest organizational unit that independently owns the assortment, pricing, maintenance, and fulfillment responsibility for a set of materials.
Prices are attributes of
(material, supply_org), not ofmaterialalone. Reports that compare prices across supply-orgs are legitimate. Reports that assume a material has “a price” without qualifying the supply-org are wrong by construction.
SAP canonical: this is Plant
Section titled “SAP canonical: this is Plant”In SAP canonical semantics, the concept is Plant (WERKS). In SAP’s MBEW (Material Valuation) table, the primary key has been (Material, Plant) since the 1970s, because:
- A plant is the smallest unit where one team is responsible for maintaining master data
- A plant holds stock independently
- A plant makes pricing decisions independently
- A plant is the atom of financial valuation for the balance sheet
The French-localised hospital system we integrate against labels the column “Société Code” — a localisation quirk. The semantic reading is Plant.
Generalization test — does the concept travel?
Section titled “Generalization test — does the concept travel?”| Domain | Supply-org |
|---|---|
| Manufacturing | Plant. Same bolt has different costs at Plant A vs Plant B because they negotiated with different vendors. |
| Retail | Distribution centre. Same SKU has different landed cost at EU-DE1 vs EU-FR3. |
| SaaS | Tenant. Same SKU has different list price per tenant. |
| Hospital | Internal supplier. Pharmacy (PHA) vs Logistics warehouse (LOG). Each has its own assortment, prices, and catalogue maintainers. |
In all four, the correct pricing grain is (material, supply_org). A flat material-only grain is a lossy projection that only works when every material has exactly one supply-org.
Why supply_org (not plant_code, not source_org)
Section titled “Why supply_org (not plant_code, not source_org)”| Candidate | Verdict |
|---|---|
plant_code | SAP-canonical, but “plant” sounds manufacturing-specific in English |
supply_org | Chosen — generic, unambiguous, distinct from external supplier |
source_org | Vague, could mean data source |
supply_point | Less standard |
fulfillment_org | Clunky |
supply_org travels well across domains and does not collide with external supplier_id (vendors outside the organization).
Model Changes (additive only)
Section titled “Model Changes (additive only)”New column: supply_org
Section titled “New column: supply_org”Added to three gold tables:
gold_article_catalogues— the supply-org that owns this catalogue entry. Sourced from the OPALEmaterial_sourcing.csvmapping (which already usesPHA/LOGnatively). The raw SZO codes (1090, 2011) are preserved ingold_supply_orgsassupply_org_code.gold_material_movements— the supply-org that delivered this movement. Sourced from the E5 CSVsociété_code.gold_price_series— the supply-org dimension of the price. The step function key becomes(material_id, supply_org, catalogue_id, effective_from).
New lookup: gold_supply_orgs
Section titled “New lookup: gold_supply_orgs”A lookup table of canonical supply-org values:
| Column | Description |
|---|---|
supply_org | Canonical ID — PHA, LOG, … |
supply_org_code | Raw source-system code (e.g. 1090, 2011) |
display_name | Human-readable name per locale |
New price rule: supply_org_matched
Section titled “New price rule: supply_org_matched”Added to the rule palette in gold_article_price_palette alongside the existing rules. The rule returns one price per (material, supply_org) pair, not one price per material. Resolution against a movement requires both keys.
New metering columns in gold_io_metering
Section titled “New metering columns in gold_io_metering”amount_in_supply_org— input side joined on bothmaterial_idandsupply_orgamount_out_supply_org— output side (E4 billing), using the learned-projection fallback (see below)
The existing amount_in_default, amount_in_highest, etc. stay untouched. Users can select any rule in Price Lab and compare them side by side.
What Stays Unchanged
Section titled “What Stays Unchanged”gold_article_price_palettekeeps all existing rules. No rule is modified or removed.gold_billing_priceskeeps all five billing rules.gold_price_comparisonkeeps its existing columns. A newmaster_supply_orgcolumn is added (additive).- All existing signals, perspectives, reports, and Price Lab charts continue to work with the old rules.
- The
channel_codederivation stays — it remains useful as a semantic classification for filtering (e.g. “show me all medications”).
Users who want the old behaviour get the old behaviour. Users who want correct pricing pick supply_org_matched.
The E4 (Billing) Fallback
Section titled “The E4 (Billing) Fallback”For E5 consumption/charging events, the movement carries supply_org and we can join directly. For E4 billing events, the billing record does not know which supply-org fulfilled the delivery — this information is lost in OPALE between movement and invoicing.
The pragmatic fallback: derive a per-material “best guess” supply-org from the E5 history.
supply_org_learned(material) := mode(supply_org) over E5 movements for that materialTracked as a new rule supply_org_learned. It gives one price per material, chosen by “which supply-org most often delivers this material”. Deterministic, works with today’s data, loses per-event precision but recovers most of the accuracy gap. A more granular alternative (cost-centre → supply-org mapping) is noted for follow-on work.
Known Caveats
Section titled “Known Caveats”The default rule is non-supply-org-aware
Section titled “The default rule is non-supply-org-aware”The existing if_only_one_active_then_this_else_latest rule (and its siblings) do not handle supply-org correctly and produce randomly-incorrect prices when a material is carried by more than one supply-org. In Price Lab tooltips, catalogue documentation, and the design docs, the old rules are marked non-supply-org-aware. They are not removed — they remain useful for materials carried by exactly one supply-org, and for comparing price drift regardless of org.
Not all source data has supply-org
Section titled “Not all source data has supply-org”Some tenants may not expose supply-org in their CSVs at all. In that case:
supply_orgisNULLfor all catalogue entries and movementssupply_org_matcheddegrades gracefully to thedefaultrule- No regression for existing tenants
Supply-org is not always well-defined
Section titled “Supply-org is not always well-defined”In some tenants, the same team maintains all catalogue entries across “plants” — e.g. a small hospital with one pharmacy serving all sites. Supply-org still exists but collapses to a single value. The new rule becomes a no-op, which is correct.
Why SAP Would Do This
Section titled “Why SAP Would Do This”The test of whether a choice is foundational or a hack: would SAP do it this way? Would Oracle EBS do it this way (as Inventory Organization)? Would any ERP serious about multi-plant pricing do it this way?
Yes, yes, and yes. Then so should we.
Related
Section titled “Related”- Pipeline Overview
- Explorer Guide — Price Lab
- Design doc:
docs/design/supply_org_pricing.md