---
title: "Why your ERP and your e-shop disagree about stock"
description: "The synchronization failure every store eventually encounters: two systems that both believe they own the stock number, and the patterns that resolve it."
author: "Luboš Zápotočný"
published: "2026-06-11"
language: "en"
canonical: "https://zapolu.com/blog/erp-eshop-stock-mismatch/"
---

# Why your ERP and your e-shop disagree about stock

Sooner or later every shop with an ERP encounters the same support
ticket: the storefront says five in stock, the warehouse says zero,
and a customer has just paid for something that doesn't exist. The
support team treats it as a one-off error, but it traces back to an
architecture decision nobody remembers making.

## The root cause is ownership

When the numbers diverge, everyone debugs the synchronization script.
But the script is usually doing exactly what it was told; the problem
is that two systems both believe they own the stock number. The ERP
decrements when it books an order. The shop decrements when checkout
completes. Both are right by their own rules, and the sync job has no
rule for deciding which value prevails.

The fix is one principle: **one system of record per fact.** Stock
truth lives in exactly one place, almost always the ERP or the WMS,
because that's where stock is booked and where goods physically move. The shop holds a cached
copy for display and a *reservation*.
Once that's explicit, every sync question has an answer: when in
doubt, the system of record wins.

## The recurring failure modes

With ownership settled, the remaining bugs are mechanical, and they
repeat across platforms and ERPs:

- **Lost updates.** The ERP pushes a stock change, the shop is mid-
  deploy or rate-limits the call, and nobody retries. Weeks later
  someone asks why one SKU has been "out of stock" all month.
- **Duplicate deliveries.** The same webhook fires twice (webhook
  delivery is typically at-least-once, so [the same event can arrive
  more than once](/blog/webhooks-retry/)), and the
  handler decrements stock twice.
- **Full syncs racing deltas.** A nightly full import and event-driven
  updates run against the same rows; whichever finishes last wins,
  and it's sometimes the stale one.
- **Mapping drift.** A SKU renamed in the ERP silently becomes a new
  product in the shop; the old one keeps its stale count forever.
- **Bundles and kits.** The shop sells a set; the ERP counts
  components. If the set-to-component conversion lives in a
  spreadsheet, expect it to be out of date.

## The patterns that resolve it

The integration layer that stops producing these tickets looks the
same regardless of which ERP is behind it:

1. **Deltas plus reconciliation.** Event-driven updates for speed, and
   a periodic full comparison that finds what the events missed. The
   reconciliation is what makes the fast path trustworthy.
2. **Idempotent consumers.** Every update carries an identifier the
   consumer deduplicates on, so processing it twice changes nothing. This one property absorbs
   retries and replays. Concurrency conflicts are a separate problem:
   lost updates and full-syncs-racing-deltas need version stamps or
   sequence numbers so a stale write can't overwrite a newer one.
3. **Correlation IDs end to end.** When order 18342 never reached the
   warehouse, "where did it go" should be answerable from the logs in
   a minute or two.
4. **Alert on divergence.** A sync that reports
   success while numbers drift apart is worse than one that fails
   outright.
   Compare aggregates on a schedule and page someone when the delta
   grows.
5. **Reservations with expiry.** If your setup reserves stock early,
   a cart holds it for minutes and an unpaid order for longer; by
   default, though, reservation happens at order placement, not
   add-to-cart. Either way, everything expires back to the
   system of record. Overselling during campaigns usually traces to
   reservations that never expire or never existed.

None of this depends on the vendor. We've built the same
architecture against NetSuite, Business Central, Odoo, and the Czech and Slovak
systems like Pohoda and Money S5 that international connector vendors
tend to ignore. That long tail is a large part of why
[our integration work](/services/integrations/) exists.

If your shop and your ERP currently disagree about stock, the place
to start is making the ownership question explicit. If you want
help, [contact us](/contact/). Deciding where each fact
lives is [system design](/services/system-design/); once that
decision is made, building the sync is straightforward.