A Riyadh-based food distributor running Dynamics 365 integrates a B2B buyer portal and, three weeks after go-live, discovers that orders placed through the portal are updating inventory positions in the ERP before the warehouse has picked them. The inventory numbers are wrong. The ERP has lost its status as the source of truth. The integration is rolled back. The portal is shut down. This failure is not unusual — and it originates before a single line of integration code is written.

The question every integration starts with

Most mid-to-large distributors in Egypt and Saudi Arabia operate an ERP — SAP, Oracle, or Dynamics 365. These systems were designed to be the source of truth for customers, inventory, pricing, and orders. They are good at that function. What they were not designed for is buyer-facing ordering: the interface a purchasing manager at a supermarket chain in Jeddah uses to place a weekly restocking order at 11pm, or the portal a restaurant group in Cairo uses to reorder ambient products across twelve locations.

When a distributor adds a self-service ordering layer, two systems now coexist around the same data objects. For every object — customers, SKUs, pricing, inventory positions, orders — one system must be authoritative. The integration question is not how to connect the two systems. It is: for each data object, which system owns it, and in which direction does data flow?

Getting this wrong does not produce a bad integration. It produces a data ownership conflict — a state where neither system is reliably accurate, both require manual reconciliation, and operations staff maintain a third truth in a spreadsheet. This is the condition that forces rollbacks. The technical failure comes later. The architectural failure happens in the design session.


What locks: the ERP holds it, permanently

Certain data must remain exclusively ERP-authoritative. The ordering layer reads this data. It does not write to it, does not maintain its own copy, and does not allow buyer-side actions to overwrite it. Three objects belong in this category without exception.

Customer master data is the first hard lock. The ERP holds the canonical record of each buyer: their legal entity, credit terms, payment conditions, assigned commercial tier and trade channel, and contract status. The ordering layer reads this record to personalize the buyer's catalog and apply their contracted pricing. If a buyer updates their delivery address in the portal, that update is staged for ERP review — it does not overwrite the ERP customer record. The ERP holds the lock. The portal reflects it. Any architecture where the portal can write back to customer master data will produce divergence within weeks, as portal-side updates and ERP-side updates accumulate without reconciliation.

Pricing configuration is the second lock. In a correctly integrated architecture, the ordering layer reads its pricing data from the ERP: tier base prices, contract overrides per SKU, volume discount tier structures. The ERP is the origin of every price the buyer sees at cart. The ordering layer applies its pricing engine logic on top of ERP-sourced data — calculating net prices through base price, contract override, volume tier, promotional override, and manual override — but it does not author the pricing data itself. The contract override lives in the ERP. The ordering layer derives the net price from it. Pricing configuration that exists in the ordering layer independently of the ERP is pricing configuration that will diverge from the ERP, invisibly, over time.

Inventory ATP — available-to-promise — is the third lock, and the naive implementation gets it wrong in one of two directions. A synchronous query against the ERP's live inventory position at the exact moment of cart confirmation removes the risk of a stale mirror, but it ties the buyer's checkout to the ERP's uptime and response time — exactly the dependency that fails hardest during the ordering peak, when on-premise ERPs go down more often than at any other time. The alternative failure mode is worse: a local inventory mirror polled infrequently, which drifts from the ERP's true position and produces confirmed orders that cannot be fulfilled.

The correct pattern is neither. ATP is confirmed at cart submission against a continuously synchronized copy of the ERP's stock position — updated on every inventory event rather than polled on a timer — with a stated freshness guarantee, under 60 seconds, rather than an unstated one. The copy is never described as an estimate, because the guarantee is what makes it usable as an authoritative source. The same pattern applies to credit exposure: validated against a continuously synced balance within 5 minutes of the ERP figure, with one additional live check reserved for orders approaching the credit limit. And when the ERP itself is unreachable, ordering does not stop — orders placed during an extended outage are flagged for operations review and validated against the synced copy before the warehouse releases anything.

The locked objects share a property: they are all supplier-controlled configurations. Customers, prices, and inventory positions are defined on the supplier side and must be authoritative in the supplier's system of record. The ordering layer presents them. It does not own them.


What syncs: the ordering layer writes one thing back

Data flows from the ERP to the ordering layer for customers, pricing, and inventory — the locked objects described above. One data object flows in the opposite direction: confirmed orders.

When a buyer submits an order through the portal and it passes cart validation — MOQ, pack type, ATP, and pricing all confirmed — the order record is written to the ERP's order management module. This is the integration's core function: converting a buyer-originated order into an ERP-native order that the warehouse team, the finance team, and the logistics operation can act on. Everything that happens after order submission — picking, dispatch, delivery, invoicing — runs in the ERP. The portal's role ends at the moment the order is confirmed.

The confirmed order must be written to the ERP as an immutable record. The warehouse does not edit it back through the portal. Adjustments that occur during fulfillment — a short-pick due to a warehouse discrepancy, a damaged case at loading, a temperature rejection at the receiving dock — are recorded as ERP adjustments against the original order, not as changes to the portal order record. The portal reflects the confirmed order. The ERP records what was actually fulfilled. The invoice is generated from the ERP's fulfillment record. This separation is not a workaround. It is the correct architecture for each system doing what it was designed to do.

The adjustment cycle is where integration architectures most often fail in practice. A distributor whose warehouse supervisors are trained to update order quantities in the portal after fulfillment changes — because the portal is the interface they are most familiar with — will produce an ordering layer that contains fulfillment corrections rather than confirmed orders. The data is now mixed, authoritative for neither function, and cannot be reliably used for buyer order history, reorder generation, or ERP reconciliation. The fix is not a user training programme. It is a clear interface policy: the portal is for placing and tracking orders, not for recording what was delivered. Delivery records belong in the ERP.

The portal is for placing and tracking orders. Delivery records belong in the ERP. Mixing these two functions in a single interface produces data that is authoritative for neither.


What this looks like: a Jeddah frozen foods distributor

Take a Jeddah-based frozen foods distributor running SAP and serving eighty-plus modern trade accounts across Riyadh and Jeddah. Before writing any integration code, the implementation team needs to make one decision early: no code gets written before the data ownership map is complete. Three decisions have to be resolved in writing first — what data SAP owns exclusively, what data the portal queries at runtime, and what data the portal writes back to SAP.

A data mapping exercise like this routinely surfaces conflicts that daily operations have been quietly absorbing — a Regional Partner tier discount partially overridden by a time-bound promotional rate that officially expired but was extended informally, updated in the sales team's shared price list document but never corrected in SAP. Buyers on the affected tier end up on manually adjusted invoices for months, covering the gap between what SAP calculates and what the sales team promised. A conflict like this stays invisible in daily operations right up until clean SAP pricing data has to serve as the sole authoritative input to a deterministic pricing engine — which is exactly when it becomes impossible to ignore.

Resolving conflicts like this in SAP before the portal goes live is what makes the rest of the architecture work. Buyers then see their actual contracted net prices, derived directly from clean SAP data through the pricing engine, with no manual adjustment at invoice time. The ERP becomes the single source of truth it was always supposed to be — because the integration forces the question of ownership that daily manual operations otherwise let a team avoid.


What the ordering layer controls — and the ERP should not

The ordering layer is not passive in this architecture. It controls the buyer experience, the ordering rules, and the order-side exception workflow — capabilities the ERP was not built for and should not be expected to provide.

Catalog visibility and assortment rules are managed in the ordering layer's Admin Console, not in the ERP. Which SKUs a given commercial tier or trade channel can see, which products are restricted for regulatory reasons in Saudi Arabia, which are excluded from specific buyers for commercial terms — these rules are configured centrally and applied at the catalog display level. The ERP holds the product master. The ordering layer controls who sees what, and when. This separation is important because assortment rules change frequently — new regulatory exclusions, tier- or channel-level commercial decisions, seasonal SKU availability — and those changes must be manageable without ERP development work.

MOQ and pack type enforcement is an ordering layer function. The ERP holds MOQ configuration as reference data. The ordering layer reads that configuration and applies it at cart — blocking an invalid quantity before the buyer submits, with a clear correction prompt showing the nearest valid quantity. This enforcement happens in the portal because the ERP does not have a buyer cart. The enforcement point is where the buyer is interacting with the system. The ERP data makes the enforcement possible. The ordering layer makes it happen.

Order lifecycle visibility — the buyer's view of their order from submission through to delivered and invoiced — is also an ordering layer function. The ERP generates the warehouse pick, the shipment record, and the invoice. The ordering layer queries these status events and presents them to the buyer as a readable order timeline: Order Received, Processing, Preparing Shipment, Out for Delivery, Delivered, Invoiced. The ERP does not build that view. It does not need to. The ordering layer translates ERP status codes into buyer-facing language, which means the ERP's internal terminology and process codes never need to be exposed to the buyer directly.

Emdaad's integration architecture reflects this separation throughout. The ERP is the source of truth for customers, inventory, and pricing. The ordering layer queries those sources at the moments they are needed — catalog display, cart validation, order confirmation — and writes a single record back: the confirmed order. Each system does what it was designed for. The integration defines the boundary between them — and holds it.

For distributors evaluating a move from manual or ERP-only ordering to a self-service buyer portal, the integration design conversation should happen before the vendor selection conversation. The question of what locks and what syncs is not a technical implementation detail. It is the decision that determines whether the integration holds up at month three — or gets rolled back at week three.