Aller au contenu

Supply-Org Pricing

Ce contenu n’est pas encore disponible dans votre langue.

The correct pricing grain is (material, supply_org) — not material alone.


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.


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.


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 of material alone. 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.

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?”
DomainSupply-org
ManufacturingPlant. Same bolt has different costs at Plant A vs Plant B because they negotiated with different vendors.
RetailDistribution centre. Same SKU has different landed cost at EU-DE1 vs EU-FR3.
SaaSTenant. Same SKU has different list price per tenant.
HospitalInternal 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)”
CandidateVerdict
plant_codeSAP-canonical, but “plant” sounds manufacturing-specific in English
supply_orgChosen — generic, unambiguous, distinct from external supplier
source_orgVague, could mean data source
supply_pointLess standard
fulfillment_orgClunky

supply_org travels well across domains and does not collide with external supplier_id (vendors outside the organization).


Added to three gold tables:

  • gold_article_catalogues — the supply-org that owns this catalogue entry. Sourced from the OPALE material_sourcing.csv mapping (which already uses PHA/LOG natively). The raw SZO codes (1090, 2011) are preserved in gold_supply_orgs as supply_org_code.
  • gold_material_movements — the supply-org that delivered this movement. Sourced from the E5 CSV socié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).

A lookup table of canonical supply-org values:

ColumnDescription
supply_orgCanonical ID — PHA, LOG, …
supply_org_codeRaw source-system code (e.g. 1090, 2011)
display_nameHuman-readable name per locale

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.

  • amount_in_supply_org — input side joined on both material_id and supply_org
  • amount_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.


  • gold_article_price_palette keeps all existing rules. No rule is modified or removed.
  • gold_billing_prices keeps all five billing rules.
  • gold_price_comparison keeps its existing columns. A new master_supply_org column is added (additive).
  • All existing signals, perspectives, reports, and Price Lab charts continue to work with the old rules.
  • The channel_code derivation 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.


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 material

Tracked 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.


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.

Some tenants may not expose supply-org in their CSVs at all. In that case:

  • supply_org is NULL for all catalogue entries and movements
  • supply_org_matched degrades gracefully to the default rule
  • No regression for existing tenants

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.


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.


jazzisnow jinflow is a jazzisnow product
v0.45.1 · built 2026-04-17 08:14 UTC