15 findings, ordered by commercial weight· Evidence from the live BC tenant, 11 Aug 2026
Replaces: the audit deck nobody opens twice, and the spreadsheets built around it
4,5004,500item locations priced below cost or with no cost at allRead the finding
1.5%1.5%of the forecasting engine's buying advice reaches an orderRead the finding
41.541.5average days late across 400 released ordersRead the finding
0 of 50 of 5approval workflows running, since June 2025Read the finding
Ask the findingsOne question, answered from the figures on this page and nowhere else.15 findings, all cited
The answer is built only from the figures above, each of which names the file and row it came from. Where the page does not hold an answer it says so rather than reaching for one, and no figure is ever estimated, extrapolated or rounded for effect.
What the evidence says is going wrong, ordered by what it costs, each with the action that closes it. Every number names the file it came from.
01Pricing and marginHighest impact
MSC are selling part of the range at a loss and the system does not stop them.
Defects by count, against 70,400 item locationsNetstock data quality dashboard, 11 Aug 2026. The 6.4 percent is the two red rows, 4.5k over 70.4k. Missing descriptions are a separate defect and are not in that count.
What to do
A margin floor on the price record, checked at the point of sale. It turns silent defects into a queue somebody clears once.
Why it ranks 01
Ranked first: it loses margin on every affected line today, and nothing stops it.
MSC pay for a forecasting engine and then buy almost none of what it recommends.
5,300supplier recommendations generated in six months
78downloaded and keyed into BC by hand
1.5%of the engine's buying advice that lands
0transfer recommendations used
Six months of recommendations, and what happened to themThe six months to 11 Aug 2026, across three buyer and planner users.
Why it matters
Every recommendation that does not land is stock bought late or cash tied up in stock nobody needed.
What to do
Bring the recommendation in as a working queue rather than a download. Close the transfer gap first, it is the cheapest stock MSC already own.
Why it ranks 02
Ranked second: the licence and three planners are paid for, then overruled by hand.
Source · msc-bc-audit/pain-points-and-opportunities.csv rows PP-018 (78 of approx 5.3k downloaded in 6 months) and PP-017 (0 DC orders downloaded in 6 months while BC shows daily transfers); netstock-used-unused-matrix.csv rows 6 and 7; bc-netstock-integration-map.csv row 4 (no automated write back observed, 3 buyer and planner users).
03Pricing and marginHighest impact
Two customers can hold two different prices for the same product and nothing in Business Central raises it. The pricing work already built finds them, and it treats a ten per cent spread as the point where somebody should look.
30%spread the rule calls high severity
15%the medium band starts here
2prices minimum before a SKU is looked at
max, minthe spread is measured against the lowest price
Severity bands, as the rule already defines themThe bands are the code's own, not a proposal. Variance is (max minus min) divided by min, so a line sold at 100 and at 200 reports 100 per cent.
Why it matters
Two buyers comparing notes is how this gets found today. The rule to find it first already exists and it is ours.
What to do
Run it on a schedule, group it by SKU and put the output in front of the person who owns the price. Carry the caveats with it: the rule buckets on the SKU alone, so unit of measure, currency, quantity break and effective date are not yet part of the key.
Why it ranks 03
Ranked third: it is the defect the pricing project was built to find, and the rule is already written.
Source · msc-data-project lib/pricing/discrepancyRules.ts lines 7 to 11 (variancePct >= 30 high, >= 15 medium, else low), line 15 (minVariancePct default 10), line 42 (a SKU needs at least 2 prices), lines 59 to 61 (averages to 4 decimal places, variance to 2). Same default at pages/api/reports/discrepancies.ts lines 13 to 14. Bucketing on SKU alone is visible in the same file.
04Pricing and marginHigh impact
The pricing estate behind the ladder runs to roughly 650,000 sales price lines, and the comparison that checks them holds no tolerance at all.
2,600pages per segment to read them, at 250 a page
4decimal places the unit price is held to
0tolerance in the price comparison
80price lists deep filled on every sync run
Why it matters
An exact comparison at that scale is honest but unforgiving. A rounding difference of a hundredth of a penny reads the same as a real conflict, so the tolerance is a business decision that has not been taken yet.
What to do
Set the tolerance where the business wants it, in one place, and state it on every report that uses it. The platform carries the figure so a finding cannot be argued with.
Why it ranks 04
Ranked fourth: it sizes every other pricing finding on this page.
Source · msc-data-project pages/reports/price-conflicts.tsx line 314: "Per-segment OData page cap. ~650k lines at pageSize 250 needs >= 2,600 pages per segment." This is the project's own working figure, written to size a page cap, and it is the only scale statement the repository commits. lib/pricing/comparePriceLists.ts line 39 compares with === and no tolerance. prisma/schema.prisma holds BcPriceLineCache.unitPrice as Decimal(12,4). lib/bc/sync.ts line 235 caps the page size at 250 and line 238 targets 80 price lists a run.
05Pricing and marginHigh impact
The price a customer pays is decided by a ladder where the first matching rule wins, not the best one, so a customer can hold a better rule further down and never get it.
13levels of price precedence, evaluated in order
13pricing methods in scope, cost plus to indexed
1stmatching rule wins, best or not
The top of the ladder, and where the everyday price sitsBars show position in the ladder, not value. Levels 2, 3, 5, 6, 8, 9, 11 and 12 sit between these, listed in the scope.
Why it matters
A ladder this deep is an asset only if a sales manager can read it before approving a discount.
What to do
Show the winning level on every priced line, by name, with the rule that beat the others. Scope chapter 14.4 already requires the record, so this needs a screen rather than a build.
Why it ranks 05
Ranked fifth: it sits under every priced line and nobody can explain a single one.
Source · docs/reference/product-scope-pricing-authority.md lines 9 to 27 (the thirteen levels, first applicable rule from 1 to 13 wins, not a best-price search) and lines 36 to 49 (thirteen pricing methods; price governance fields required by scope chapter 14.4). Source authority: mCentral OPS Product Scope, chapter 14, pages 24 and 25.
06Order to cashHigh impact
Four hundred released orders are running weeks behind and nobody in the business can say whether that is a real backlog or stale data. That is the finding.
400released orders showing as delayed
41.5average days of delay
99showing ready to ship
50showing simply open
The sales role centre, as it stood on 11 Aug 2026Role centre cues, 11 Aug 2026. The audit recorded its confidence as medium: the count is observed, the interpretation is open.
Why it matters
A released order is one the customer believes is coming. If it is stale data instead, the board is being shown a backlog that does not exist.
What to do
Put the delayed queue on a screen with a name against every line and triage it once.
Why it ranks 06
Ranked sixth: revenue promised, costed and not yet billed.
Source · msc-bc-audit/pain-points-and-opportunities.csv row PP-009 (400 sales orders released not shipped, average days delayed 41.5, confidence medium, count observed and interpretation open); functionality-register.csv row SAL-002 (cues: 50 open, 99 ready to ship, 400 delayed, avg days delayed 41.5).
07ControlHigh impact
Approvals, credit control and supplier payments have quietly left the system. The controls are still written down, they have simply not run for over a year.
5approval workflows defined, none enabled
Jun 2025notification dispatcher went into Error
Jun 2025payment journal went stale
Neverreminders issued, statements job on hold
Why it matters
Credit control run from somebody's inbox means no audit trail, no ageing anyone can rely on and cash collected late.
What to do
Rebuild the ladder where the work already is, created and issued as different states. Xero keeps the ledger, the platform runs the chase.
Why it ranks 07
Ranked seventh: cash collected late, no audit trail, and it has held for a year.
Source · msc-bc-audit/pain-points-and-opportunities.csv rows PP-021 (dispatcher in Error since 12/06/2025, approvals stall silently for over a year) and PP-013 (payment journal stale since 24/06/2025); workflows/07-credit-control.md line 6 (reminders created, issued reminders never generated); 02-environment-and-modules.md line 44 (5 workflows defined, none enabled).
08Buying and stockHigh impact
A high data quality score is hiding the field the forecast leans on hardest. Most of the estate has no lead time at all.
97.75%the vendor's own data quality score
19,900item locations with no lead time
3,000with no supplier link
233overdue purchase orders
The gaps behind the scoreIssue counts published beside the 97.75 percent score, same dashboard, 11 Aug 2026.
Why it matters
Lead time drives safety stock, so a missing one becomes an avoidable stockout or money sitting on a shelf.
What to do
Make lead time a required, owned, dated field on the item and supplier record, then manage the gap count instead of the vendor's score.
Why it ranks 08
Ranked eighth: it is the input the buying decisions above depend on.
Source · msc-bc-audit/netstock-functionality-register.csv row NSF-012 (score 97.75 percent with itemised issue counts); pain-points-and-opportunities.csv row PP-019, confidence high; system-of-record-matrix.csv row 4 (lead times sourced from ERP, 19.9k gaps to fix in migration).
09Pricing and marginHigh impact
MSC govern what they sell for and not what they pay. Discount posting is switched off on both sides.
S-PLsales price list, last used 10 Jul 2026
PPLpurchase price list, never used
No Discountsposting setting, sales and purchases
Price list series in the tenant, by state
Active, sales S-PLStale, jobs J-PL, last used 11 Jul 2024Never used, purchases PPL
The three price list number series in the tenant, read on 11 Aug 2026.
Why it matters
With cost on vendor cards and discounts unposted, margin is something a person reconstructs in a spreadsheet after the invoice has gone.
What to do
Hold cost and price as one governed record, landed cost and rebates netted off, and show live margin on the quote. Price authority already exists, this is its missing half.
Why it ranks 09
Ranked ninth: it is why the margin defect above cannot be measured.
Source · msc-bc-audit/number-series-usage.csv rows 106 (S-PL, active, 10/07/2026), 67 (PPL, never) and 46 (J-PL, stale, 11/07/2024); data-migration-inventory.csv row 5 (S-PL active, PPL never, purchase prices possibly on vendor cards); 02-environment-and-modules.md lines 66 and 68 (discount posting No Discounts on both setups).
10ControlHigh impact
Nothing keeps the effective price for a customer and a product on a given day, so no two runs can be compared and no finding can be closed.
0effective price snapshots kept per run
1line of console output is the whole audit trail
5stages in the bulk change lifecycle, apply not yet wired
reviewedAtthe only lifecycle a finding currently has
The findings layer, as the schema declares it
Tables declared for findings 3Tables anything writes to 0
PriceComparisonResult, CrossCustomerDiscrepancy and AiRecommendationScore all exist in the schema. The reports compute in memory instead, so nothing survives the request.
Why it matters
A price question asked in a meeting cannot be answered from a report that was thrown away when the page closed. This is the piece that turns detection into governance.
What to do
Store the effective price per run, give every finding a lifecycle from open to resolved with a name against it, and persist the audit trail. The platform does all three as standard, so the rules already written keep their value.
Why it ranks 10
Ranked tenth: it is why every pricing finding above has to be found again from scratch.
Source · msc-data-project docs/EVOLUTION-TO-PRICING-AUDITOR-PLAN.md section 2 names the missing effective_prices_snapshot as the "Primary structural gap for reproducible detection SQL", and section 1 records "Formal finding lifecycle (open to resolved, audit)" as a gap with "PriceComparisonResult has reviewedAt only". prisma/schema.prisma declares PriceComparisonResult, CrossCustomerDiscrepancy and AiRecommendationScore; nothing in the repository writes to any of them. lib/audit.ts recordAuditEvent is a console.log with the comment "Placeholder for persistence sink". pages/reports/price-conflicts.tsx states "BC apply is not wired yet".
11Pricing and marginModerate
Underneath the ladder the live system prices from the site account, then the company account, then falls back to a default category without saying so.
Sell tosite account, asked first
Bill tocompany account, asked second
Materialscategory used when both are silent
Why it matters
Two sites of one customer can be charged differently and nothing on the order says so.
What to do
Make the fallback fail loudly. Every priced line names the record that priced it, and a line that reached the default waits for a person.
Why it ranks 11
Ranked eleventh: narrower than the ladder, and the fallback posts without asking.
Source · msc-bc-audit/al-source-findings.md line 35 (resolved from the customer price list, sell-to first, then bill-to, fallback item category Materials) and line 156 (lines are re-priced from the customer price group price list). Restated in docs/estate/appendix-b-bc-audit-distillation.md rows 93 and 342.
12Buying and stockModerate
The two sites are not running the same business. West turns stock nearly twice as fast as East and still misses its fill rate, which is the transfer gap showing up in service.
93%fill rate at EAST, target 96 percent
87%fill rate at WEST, same target
5.4stock turns at EAST
8.9stock turns at WEST
Fill rate against targetNetstock network performance, 11 Aug 2026. EAST approx 27.9k item locations, WEST approx 26.6k.
Why it matters
Orders nobody could serve, from a business that had the goods somewhere. The same money, in the wrong building.
What to do
Fix the transfer path before buying more stock. The engine already produces the transfer recommendations nobody uses.
Why it ranks 12
Ranked twelfth: real service loss, and the visible face of the transfer gap.
Source · msc-bc-audit/netstock-functionality-register.csv row NSF-016 (5 locations, approx 70.4k item locations, EAST approx 27.9k and WEST approx 26.6k, fill rate EAST 93 percent vs target 96 and WEST 87 percent vs 96, stock turns EAST 5.4 and WEST 8.9), confidence high; pain-points-and-opportunities.csv row PP-017.
13Order to cashModerate
The open quote cue reads at its ceiling, which is where the counter stops rather than where the quotes stop. Nobody is closing them.
999+open quotes, a ceiling not a count
Dailynew quotes raised, series last used 11 Aug 2026
Feb 2025last purchase quote, that side is retired
Why it matters
Either it is real pipeline nobody is chasing or it is noise in the forecast, and the business cannot tell which. The sales director asked during the audit and it is still open.
What to do
Give quotes an expiry and a follow up queue, then decide the cutover cleansing rule before migration rather than after.
Why it ranks 13
Ranked thirteenth: a doubt over the pipeline rather than a measured loss.
Source · msc-bc-audit/functionality-register.csv row SAL-001 (S-QUO last used 11/08/2026, Sales Quotes Open cue 999+, open quote count suggests quotes not closed or cleaned); risks-blockers-and-open-questions.md line 41; data-migration-inventory.csv row 9 (999+ open quotes, decide cutover cleansing); used-unused-matrix.csv row 8 (purchase quotes rare, last 03/02/2025).
14Pricing and marginModerate
Changing a price used to stop the sales floor for a minute at a time. The webhook that published price changes ran synchronously.
60+seconds sales users were blocked
3price list line events firing a webhook
Why it matters
When maintaining a price is painful, prices stop being maintained. That is how a range drifts into selling below cost.
What to do
Keep price publication asynchronous and make price work quick: bulk jobs with preview, approve, apply and rollback.
Why it ranks 14
Ranked fourteenth: the outage was fixed. The habit it left behind was not.
Source · msc-bc-audit/al-source-findings.md lines 107 and 112 (price and ABL webhooks on price list line insert, modify and delete; blocking sales users for 60+ seconds on price updates); customisations.csv row EXT-010 BiApiExt; docs/estate/appendix-b-bc-audit-distillation.md line 82.
15ControlScope, not a defect
The logic that makes MSC different is not in Business Central. It sits in bespoke extensions bolted around it, and all of it has readable source in the tenant.
108apps in the extension estate
18bespoke, 15 Personify and 3 ad hoc
15reseller brands needing their own layout
28enabled users, 21 named staff
The bespoke layer, by publisher
Published by Personify 15Ad hoc 3
All bespoke apps have AL source in the tenant, so the rules can be read back precisely.
Why it matters
Every rule is readable, so the replacement is bounded and quotable. The branded layouts are the tell: each new reseller today means another Word template.
What to do
Scope the replacement against the extension register, not Business Central's feature list. Manufacturing, service, jobs, fixed assets and advanced WMS are demonstrably unused. Branding is a setting, not a layout file.
Why it ranks 15
Last because it is not a defect. It is what makes the replacement quotable.
Source · msc-bc-audit/02-environment-and-modules.md lines 22 to 28 (108 apps, 15 Personify PTEs, 3 default publisher PTEs, 28 enabled users of which 21 named staff); 01-executive-summary.md lines 19 to 33; reports-and-documents.csv row 16 (15 white label delivery note layouts named); pain-points-and-opportunities.csv row PP-028.
What it adds up toIf you read one thing on this page
Stop the leak
A margin floor checked at the point of sale. Highest value change on this page, and it is a rule, not a rebuild.
Show the price
Name the winning level on every line. Thirteen levels of precedence stop being a liability and start being the asset they should be.
Buy what the engine says
Moving stock MSC already own between two sites they run is the cheapest service fix available. Today none of it happens.
Bill sooner, prove control
Late orders, uncounted quotes and stalled approvals all fix the same way: the work goes back on a screen with a name against it.
Where these figures come fromIncluding how the order was decided
Two sources, and every card names its own. The pricing work already built at gitlab.personify.tech/msc/msc-data-project, read directly, file and line quoted. And the read only audit of the live MSUPPLY LIVE Business Central tenant of 11 Aug 2026, the Netstock UI audit of the same day, the AL source read from the tenant's own extensions, and Product Scope chapter 14.
The data project publishes rules, not results. Its findings tables are declared and never written, its seed creates nothing and every count it shows is computed live and discarded, so what it contributes here is its thresholds, its comparison rules and its own record of what is still to build. Its mock fixtures are never shown as findings.
Two caveats travel with the audit numbers and both stand on their cards. The 999 plus open quotes is a display ceiling, so a floor rather than a count. The 400 delayed orders was recorded at medium confidence. The 650,000 price lines is the data project's own working figure for the size of the job, written to size a page cap, not a verified count.
The order is a commercial judgement, not a measurement. Each finding states the basis for its placing, built only from the figures cited on it. No pound value is attached to a finding the source did not price.