Skip to content

D-04

Supplier Scorecard

The supplier business review, as one pattern: a master-detail wireframe on top of a single purchase-order-line fact. Delivery, quality, and price all come off the same row, measured at your dock from your own receipts — so when a supplier disputes a score, the path from the ranking to the exact PO lines is a click, not a data pull. This page carries the wireframe, the dimensional design, and the source tables in SAP, JD Edwards, Dynamics 365, Infor M3, Oracle EBS, and Oracle Fusion.

There is a reference build of this pattern: the Supplier Scorecard dashboard in the showcase renders it over frozen, simulated data — not a live client system. Its tiles are this wireframe over one frozen, simulated purchase-order book — delivery, quality and price kept as separate numbers, with control limits deciding which suppliers are actually different.

Last verified September 2026.

What it answers

Which suppliers are hurting you — on delivery, on quality, on price — and which exact purchase order lines prove it. Three conversations that are usually three disconnected spreadsheets, answered from one fact so the quarterly business review does not open with a reconciliation argument.

One rule sets everything else: measure from your own receipts, not their claims. The default basis on this pattern is the goods receipt posted at your dock — the event your own system recorded. Supplier-reported ship confirmations are a named variant you can publish beside it if the relationship warrants, but they are not the measure: a scorecard built on the supplier's own telemetry scores the supplier's own story.

The buy side has the same switchboard as the sell side, and the same traps — so it is worth walking the six OTIF switches in the buy-side direction rather than inventing new ones. The one that always bites: the date-basis switch. Score against the first confirmed promise, not the promise as renegotiated, or every expedite call launders the lateness it was called about. Request-date OTD — did they hit the date you actually wanted — is a legitimate second number, published separately and labeled as its own thing, never blended into the first.

The wireframe

Master-detail, the same geometry the customer service pattern uses, turned around to face the supply base. The ranking is interactive: clicking a supplier filters the cuts and the line table beneath it, which is the drill every vendor conversation needs available in the room.

Supplier scorecard dashboard wireframe: four KPI tiles, supplier ranking by on-time delivery, cuts by sourcing category and receiving site, failing purchase order lines with a failure-reason context panel

OTD %
In-full %
Rejection %
PPV $
Supplier ranking by OTD (click supplier to filter ↓)
By Category
By Receiving Site
Failing PO Lines
Failure Reason: late | short | rejected | over price
Filters
KPI / Metric
Primary Chart
Secondary Viz
Detail / Table

Layout basis

  • L-03 Master-Detail / Drill-Down supplier ranking to the exact PO lines — the drill every vendor conversation needs in the room
  • L-04 Tabbed Views the alternative when delivery, quality, and price each get their own review — three tabs over one fact

What each region reads from the schema below:

  1. KPI row three count_if or ratio measures and one additive currency measure over the same fact — each tile annotated with its formula and its date basis, because a supplier OTD number without a stated basis is not a number anyone can defend in a vendor meeting.
  2. Supplier ranking dim_supplieras the group-by, sorted, with the line count visible beside every bar — a supplier with four lines and a supplier with four thousand do not belong on the same ranking without their denominators shown. The target line is the number in that supplier's agreement, labeled as such.
  3. Cuts the same flags grouped by dim_supplier sourcing category and dim_receiving_site — is this a supplier problem or a category problem, and is one of your own docks the common factor.
  4. Failing PO lines the fact's own grain, filtered to failures — the degenerate PO and line numbers carry the drill all the way to the document in the ERP, which is what makes the conversation about a document instead of about a percentage.
  5. Failure reason late, short, rejected, or over price — derived from the same flags and deltas the tiles use, so the panel and the scorecard can never disagree.

The star schema underneath

The fact is an accumulating line fact — the buy-side twin of the order-line fact on the customer service pattern. One row per purchase order line, its dates, quantities, and prices filling in as the line moves through confirmation, receipt, inspection, and invoice match. No scores stored anywhere.

fct_po_line_fulfillmentAccumulating line fact

Grain one row per purchase order line

Stored components

  • order / first_confirmed / current_promise dates
  • first_receipt / last_receipt dates
  • ordered / canceled / received / rejected quantities
  • receipt_count
  • standard_cost / po_price / invoice_price (price-unit normalized)

Foreign keys

  • dim_supplier
  • dim_item
  • dim_receiving_site
  • dim_date
dim_supplier
  • supplier
  • sourcing_category
  • payment_terms

The cut the whole scorecard exists for — one row of the ranking table per supplier.

dim_receiving_site
  • site
  • warehouse

Where the goods actually arrived — the receipt-basis anchor.

po / line numbersdegenerate
  • po_number
  • line_number

The drill path from any supplier score to the exact document.

dim_item
  • item
  • category
  • buyer

Conformed with the rest of the gold layer — the same item the inventory and service patterns cut by.

dim_daterole-playing
  • ordered
  • promised
  • received

One date dimension, three roles — which role anchors on-time is the scorecard's date-basis switch.

fct_po_line_fulfillment — dates, quantities, dispositions, and normalized prices stored per line; on-time, in-full, rejection, and price variance all derived from them at query time.

Two components deserve their own note. Keep both promise dates. The first confirmed date and the current promise are different columns, because scoring against one and reporting the other is the single most common way a supplier scorecard becomes fiction. Normalize prices on the way in. Purchasing prices are quoted per price unit — per hundred, per thousand — and the divisor lives in a different column from the price. Divide once, during the load, and store a true unit price; do it at query time and it will eventually be forgotten in one report and not another.

Measures & additivity

Five measures over the line fact — one additive, four recomputed from summed components at every grain:

Supplier scorecard measures, their formulas over the PO line fact, and their additivity
MeasureFormulaAdditivityNote
supplier_otd_pctcount_if(on_time) / count(*)Non-additivefirst_receipt_date against the promise date — measured at your dock from your receipts, not the supplier's ship confirmation.
in_full_pctcount_if(received_qty − rejected_qty >= ordered_qty − canceled_qty) / count(*)Non-additiveFirst receipt or cumulative is a named switch, same as the customer-side OTIF definition — received against ordered less canceled, with rejected quantity excluded from the received side.
rejection_pctsum(rejected_qty) / nullif(sum(received_qty), 0)Non-additiveA ratio of summed quantities at every grain — never the average of per-PO rejection rates.
ppv_amountsum((invoice_price − standard_cost) × received_qty)AdditiveThe one fully additive measure on the board — signed, from your own invoice matches.
actual_lead_time_daysmedian(first_receipt_date − order_date), with P90 beside itNon-additiveReported as median and P90 recomputed from pooled lines — a mean lead time hides exactly the tail the planners buffer against.

Two rules the board depends on. Rejection is a ratio of summed quantities, never an average of per-PO rates. A ten-piece order fully rejected and a ten-thousand-piece order fully accepted do not average to 50% of anything. Sum the rejected quantity and the received quantity first, then divide once. Lead time is a distribution, not a mean. Report the median and the P90 recomputed from the pooled lines at whatever grain is on screen — the mean smooths away exactly the tail the planners are buffering against, and a percentile re-aggregated from per-supplier percentiles is not a percentile at all.

And the target line: it is the on-time commitment written into that supplier's agreement, drawn on the chart and labeled as the agreed number. Nothing on this pattern draws an external industry benchmark — a line nobody in the room signed is a line nobody in the room is accountable to.

On Databricks AI/BI

Rendering notes for building this wireframe on Databricks AI/BI dashboards. Where a capability below comes from hands-on checking rather than documentation, it was verified in one workspace on 2026-08-24 — a point-in-time observation, not a vendor commitment; re-check in your own workspace before committing a design to it.

What this wireframe needs from Databricks AI/BI, whether the platform provides it, and how to build it
NeedStatusNote
Lead-time median and P90 recomputed from the pooled lines at the displayed scopeNativeThis is the rule the pattern rests on. Exact percentile(expr, percentage) is documented in the SQL language and is distinct from the approximate function; it also works as a metric-view measure, returning exact values at every grain tested, and PERCENTILE and MEDIAN exist as widget-level aggregations over a raw-row dataset — the metric-view measure and the widget-level aggregation both verified in one workspace on 2026-08-24. The rule: keep percentile tiles on datasets that return raw rows, or recompute through parameterized SQL. A dataset that pre-aggregates a percentile by any dimension is already wrong at the unfiltered total — the failure carried in full by the lead-time KPI pitfalls.
A small-denominator guard on the supplier league tableWorkaroundThere is no native funnel-plot type. Build it as a scatter with the control-limit curves computed as columns in the dataset SQL — structurally the same chart, since a funnel plot is a scatter plus two computed curves.
Drill from a supplier's score to the PO lines behind itWorkaroundThe same assembly the service pattern needs: cross-filtering links only widgets that share a dataset, drill-through covers a wider list of widgets including the table, and no native drill-hierarchy object exists. Build the path from shared-dataset widgets at supplier grain and at line grain.

Sourcing it from SAP

Table roles. Purchase orders from EKKO / EKPO, with the confirmed delivery schedule in EKET — that is where the promise date the scorecard measures against comes from, and where a reschedule shows up as a changed schedule line. Receipts come from EKBE, the purchasing document history, which carries goods receipt and invoice receipt rows against the line — aggregate them to the PO line before comparing, because partial deliveries are separate rows — with MSEG as the underlying material document detail. The supplier dimension is LFA1 for identity plus LFM1 for the purchasing-organization view. Quality comes out of QM: QALS inspection lots created against the receipt, and QAVE usage decisions — the accept, reject, or accept-with-deviation call that turns an inspection into a rejected quantity.

The traps. Price variance runs against standard cost, and every SAP price carries a price unit — PEINH — that must divide the price before anything is multiplied by a quantity. Skip it and a per-thousand item reports a variance a thousand times too large, which is a wrong number confident enough to reach a supplier meeting. EKET keeps the current schedule, so the first-confirmed date is either taken from change documents or captured in the lakehouse on first confirmation — that capture is a build decision, not an afterthought.

Sourcing it from JD Edwards

Table roles. Purchase orders in F4301 / F4311, and the workhorse is F43121, the PO receiver — the three-way match backbone. Its match-type flag is what makes the whole scorecard computable from one table: receipt rows carry the received quantity and date that drive OTD and in-full, while voucher rows carry the matched price that drives PPV. Filter to the wrong match type and you will double-count receipts. F43199 is the PO detail ledger — the change-order audit history, and therefore the promise-change record the first-confirmed date is reconstructed from. The supplier dimension is F0401 over F0101, and F0411 carries the A/P voucher side.

The traps. The usual JDE encoding, all of which lands directly on these measures: quantities and costs carry implied decimals (a PPV computed on raw integers is off by orders of magnitude), dates are Julian CYYDDD integers so date arithmetic has to convert first, the branch plant is space-padded (TRIM before joining), and dispositions decode through user-defined codes.

Sourcing it from Dynamics 365

Table roles. Purchase orders on PurchTable / PurchLine, where the requested and confirmed delivery dates are a column swap on the line — the date-basis switch expressed as two columns. VendPackingSlipJour is the posted product receipt: one row per receipt posted against the order, the record of when goods physically arrived. Invoiced price comes from VendInvoiceJour / VendInvoiceTrans, the supplier dimension from VendTable, and the standard-cost baseline for PPV from InventItemPrice — activated per costing version and dimension, and carrying its own price-unit divisor.

The traps. Be honest about the coverage here: the reference has no posted product-receipt line entity — the journal header is cataloged, and receipt quantity at line grain comes from the raw transaction tables underneath it. That means the receipt side of this fact is a raw-table extraction in D365, not an entity-based one, and it should be scoped that way rather than discovered mid-build. Then the standing mechanics: every table is company-partitioned (the DataAreaId quirk), unposted date columns carry sentinel dates rather than NULLs, and dispositions are integer enums that decode from metadata.

Sourcing it from Infor M3

Table roles. Purchase orders in MPHEAD / MPLINE, where the line carries the planned delivery date and the lowest/highest status pair. M3 has no separate receipt document in the reference — receipts are read from MITTRA, the stock transaction ledger, filtered to inbound movements carrying the PO reference, and reconciled against the line status ladder. Negotiated terms sit on MPAGRH, the purchase agreement header — the record the agreed on-time commitment and the agreed price come from. CIDMAS is the supplier dimension, MITFACcarries the facility's approved cost as the PPV baseline, and FPLEDG is the A/P ledger on the invoice side.

The traps. The status ladder decides what a receipt is — read the status quirk before writing the filter, or partially received and fully received lines end up in the same bucket. Pin CONO in every join, read column names as a two-char prefix plus a four-char alias (the prefixes quirk), and convert the numeric YYYYMMDD dates before doing lead-time arithmetic.

Sourcing it from Oracle EBS

Table roles. The purchase document is three tiers — PO_HEADERS_ALL, PO_LINES_ALL, and PO_LINE_LOCATIONS_ALL — and the third one is the grain this scorecard is built on: the shipment schedule, carrying NEED_BY_DATE, PROMISED_DATE, and the running received, billed, and cancelled counters — balances, not events. Receipts are two tables: RCV_SHIPMENT_LINES holds the cumulative line, and RCV_TRANSACTIONS the event ledger underneath it. AP_SUPPLIERS is the supplier dimension and CST_ITEM_COSTS the PPV baseline, one row per item per organization per cost type.

The traps. The receiving ledger is insert-only: one receipt writes a receive row and a deliver row at minimum, and corrections and returns arrive as new signed rows rather than updates — so net accepted quantity means summing the parent-transaction chain, and on-time is measured at the first event where the cumulative accepted quantity reaches the scheduled quantity. Price-break rows share the schedule table with real shipments, so filter SHIPMENT_TYPE before counting anything. Pin operating unit on the _ALL tables (MOAC in practice) and remember the receiving org is SHIP_TO_ORGANIZATION_ID, not ORG_ID (ORG_ID vs ORGANIZATION_ID).

Sourcing it from Oracle Fusion

Table roles. The same document spine, same names: PO_HEADERS_ALL, PO_LINES_ALL, and PO_LINE_LOCATIONS_ALL, with the schedule again carrying NEED_BY_DATE and PROMISED_DATE beside the received, accepted, and rejected buckets — which is what makes the delivery and quality numbers separable without leaving the schedule. RCV_SHIPMENT_LINES and RCV_TRANSACTIONS are the receipt line and its event ledger, POZ_SUPPLIERS the supplier master, and CST_STD_COSTS the standard-cost baseline for PPV.

The traps. Fusion Cloud has no direct SQL path; the physical names describe the source model and the extract must use the verified BICC/PVO surfaces available for those tables (no SQL path). Receiving stays an event ledger where one receipt writes many rows, so scope TRANSACTION_TYPE before any rate is computed. Blanket price-break rows share the schedule table here too, and this is the harder version of the EBS trap: there is no SHIPMENT_TYPE column to filter on, so agreement-sourced rows have to be excluded by joining PO_HEADERS_ALL and scoping TYPE_LOOKUP_CODE to the ordering document type. The supplier has no name column — it lives on the TCA party, so every supplier-name row on the ranking needs that join. Standard costs are date-effective and scoped by cost org and book (date-effective rows), and the PO stripe is the named PRC_BU_ID procurement business unit (_ALL means BU here).

Running the workshop

Procurement owns two decisions here, and both have to land before anyone writes SQL:

  • Agree the promise-date source. First confirmed, current promise, or your original request — three defensible choices, three different scores, and only one of them can be the headline. Write down which, and where in the ERP it is read from. If the answer is "current promise," say out loud that expedites will disappear from the score, and publish a promise-change count beside it.
  • Agree the tolerance. Early is not automatically good and one day late is not automatically a failure — the window is a commercial decision, usually already written into the supplier agreement. Take it from there rather than inventing it in the dashboard.

And the anti-patterns to name out loud: scoring against renegotiated promises (every expedite call quietly erases the lateness that caused it), averaging per-PO rejection rates (a ratio of ratios that no grain agrees with), and a scorecard with no path to the PO line — a supplier score nobody can drill into loses its first serious challenge, and it only has to lose once.

Maintained by Summit Analytics, a supply chain analytics practice. The tools and references are free — the consulting is selective.

Part of the Summit Analytics reference library.

Work with the practice

Not affiliated with or endorsed by SAP, Oracle, Microsoft, or Infor. Product names are trademarks of their respective owners.