We build ERP integrations for a living — warehouse sync for one client, stock-and-price feeds for another, marketplace bridges for several more — and the sentence we hear most often at the kickoff call is "we just need to sync products and orders." Six weeks later it's reconciliation jobs, conflict rules, retry queues and a Slack channel called #stock-mismatches. The difference between those two states isn't the API; it's the pattern you choose before you write a line of code. This page is about the patterns that hold up in production on PrestaShop specifically — which direction data flows, how the sync actually runs, and the handful of design decisions that decide whether you sleep through the night.

If you're earlier than that — still deciding whether the manual spreadsheet has become a liability worth replacing — start with ERP integration for PrestaShop: when your store outgrows manual work, which covers the readiness signals and the build-vs-buy question. This guide assumes you've decided to integrate and you want the architecture that won't bite you.

Pattern 1: decide who owns each field before anything else

The single decision that determines whether the integration is calm or chaotic is data ownership: for every field, one system is the source of truth and the other is a mirror. Get this wrong and you spend the project life of the integration writing conflict-resolution code nobody trusts. Get it right and most of the hard problems simply don't occur.

The pattern that works on the vast majority of PrestaShop builds: the ERP owns the catalogue, PrestaShop owns the order.

DataSource of truthFlowsPrestaShop entity it lands in
Products, prices, stock, categoriesERPERP → PrestaShopps_product, ps_product_shop, ps_stock_available, ps_specific_price
Orders, order statusPrestaShopPrestaShop → ERPps_orders, ps_order_detail, ps_order_history
InvoicesDepends on the accounting modelPrestaShop → ERP (usually)ps_order_invoice — but ownership varies: many accounting/ERP setups require the accounting system to issue the legal invoice number.
Customers, addressesPrestaShop (usually)PrestaShop → ERPps_customer, ps_address
The same field, both waysAvoid. Bidirectional sync on one field (prices is the classic offender) forces a conflict policy. Every project we've watched try it ended up with a "last write wins" rule that silently overwrites someone's correct value.

So what does this buy you? When the warehouse changes a price in the ERP, it propagates to the shop and nobody on the shop side can accidentally fight it. When a customer places an order, PrestaShop is unambiguously right and the ERP just records what happened. There's no "which number is correct" argument at 2am because, by design, only one system is ever allowed to be the author of a given field.

Pattern 2: pick the sync mechanism that fits your stock velocity

Once direction is settled, the next pattern is how the data moves. There are three honest options. None is wrong — they suit different shops, and the deciding factor is almost always how fast your stock turns over.

MechanismHow it runsLatencyOperational costBest when
Batch (scheduled)Cron pulls rows changed since last runMinutesLow — a cron and a log tableUnder ~50k SKUs, normal order volume. Our first reach on ~80% of builds.
Event-driven (webhooks)A change fires a webhook into a queue + workerNear real-timeHigh — queue, worker, dead-letter store, retry policyFlash sales, fast stock, omnichannel where a POS can sell between two cron runs.
Middleware layerAn intermediate service (n8n, MuleSoft, custom) transforms and routesDepends on the middlewareMedium-high — one more thing that can go downThe ERP also feeds accounting, a PIM and a marketplace; PrestaShop is one of several consumers.

Batch sync, done properly on PrestaShop

Boring is a feature. A cron job every N minutes, pulling rows changed since a stored high-water mark, is predictable and easy to debug when it breaks at 03:00. On PrestaShop you have two clean ways to schedule it: a module registered against the actionCronJob hook (driven by the official ps_cronjobs / Cron Tasks Manager module), or a plain system crontab calling a CLI script — php bin/console on 1.7+/8/9, or a guarded controller endpoint. We prefer a CLI script over a web-triggered URL: it can't be hit by a crawler, it doesn't time out under PHP's web limits, and it logs to a file you control.

The thing that makes batch sync reliable is the high-water mark: store the marker of the last successful run and only pull rows changed since then. A timestamp alone is fragile — when several rows share the same date_upd, or a transaction commits late, a pure timestamp cursor can skip changes. Pair the timestamp with a stable ID cursor (timestamp plus last-seen id), add a small overlap window that re-reads the last few seconds, or use the ERP's change sequence where it exposes one. The first run is a full load; every run after is a delta. Don't re-scan the whole catalogue every fifteen minutes — that's how a sync that was fine at 5,000 products falls over at 50,000.

Event-driven, and the honest cost of it

Webhooks give you near-real-time consistency, but you've now signed up to operate a queue, a worker, a dead-letter store and a retry policy. On the PrestaShop side, the natural emit points are core hooks: actionOrderStatusPostChange when an order moves status, actionValidateOrder when one is placed, actionUpdateQuantity when stock shifts, actionProductUpdate for catalogue edits. Your module listens on these and pushes a message outward rather than calling the ERP synchronously inside the request — never block a customer's checkout on an ERP round-trip that might be slow or down.

If your real need is "when X happens in the shop, do Y in another system" and you don't want to own queue infrastructure, a no-code automation platform may be the right pattern instead of a bespoke webhook stack — see Zapier and Make for PrestaShop for the architecture and PrestaShop and Zapier: automating workflows without writing code for the step-by-step. They're a genuinely good fit for low-to-moderate event volume and order-stage notifications.

Pattern 3: the design decisions that keep the integration honest

The mechanism gets the data across. These three decisions decide whether it stays correct over months, and they're where most home-grown integrations quietly rot.

Match on external IDs, never on names

Store the ERP's reference alongside PrestaShop's own id_product. We typically add an erp_reference column (or reuse the existing reference / EAN13 field if it's already maintained cleanly), and we keep the mapping in a dedicated table rather than scattering it. Matching by product name is how you end up with two "Black T-Shirt" rows merged into one. Matching by SKU alone fails the morning someone tidies the SKUs in the ERP. A stable, opaque external ID survives both.

Make every operation idempotent

Every sync operation must be safe to repeat, because at some point it will repeat — a retry fires, a worker re-processes a message, someone re-runs last night's job. We use a sync_log table with a unique key that pins down the specific change, not just the resource — (resource, external_id, operation) on its own is too coarse, because two legitimate updates to the same product would collide and the second would be silently dropped. Add the source event/change ID (or a version, timestamp hash, or payload checksum) to the key — e.g. (resource, external_id, operation, source_event_id) — so a genuine new change is always applied while a literal replay of the same change hits the unique constraint and becomes a no-op. So what? It means a re-run never double-decrements stock or double-imports an order, so you can retry aggressively without fear — and retrying aggressively is exactly what you want when a transient ERP timeout would otherwise lose data.

Log every attempt — it's your evidence

One row per sync attempt: what, which direction, success or failure, a payload hash, a timestamp. The first time the warehouse phones to say "you sent us the wrong stock figure," this table is the only thing on earth that tells you whether they're right. Keep it even when you're cleaning a store up for go-live: if you're clearing test orders before launch, do it in a way that preserves the log — that audit trail is worth more than the disk it costs.

Stock sync: the pattern that prevents oversells

This is where every ERP integration earns its budget. Oversell once during a busy period and the support and goodwill cost dwarfs whatever the integration saved all year. The pattern we use on every build, regardless of mechanism:

  • The ERP pushes stock from the system that owns it at a calm interval — matched to how fast that catalogue actually moves. For smaller catalogues a full (or partitioned) snapshot every 5–15 minutes is simple and self-correcting. For large catalogues a full snapshot that often is unrealistic and can overload PrestaShop, so use deltas or a change feed with a periodic full reconciliation to catch any lost message. A snapshot or reconciliation is self-correcting; a stream of deltas alone drifts the moment one message is lost.
  • PrestaShop decrements stock locally the instant an order is placed, in ps_stock_available, without waiting for the next ERP poll. The shop must never sell stock it has already promised, even if the ERP is mid-cycle.
  • A nightly reconciliation job compares the two and reports drift. Critically, it does not blindly auto-correct catalogue-wide. Auto-correct only where ownership and thresholds are unambiguous — a small, bounded delta from the system that clearly owns that figure — and otherwise alert a human. Blind catalogue-wide auto-correction is how a single bug becomes a stock reset at 03:00, where the cure does more damage than the disease.
  • Drift past a threshold alerts a human. A person decides whether the ERP or PrestaShop is the truth for that SKU, because the answer genuinely varies and a script can't know which side made the mistake.

If overselling is your acute pain and you're not ready for a full ERP project, PrestaShop's built-in stock management (Catalog → Stock on 1.7/8/9, or legacy Advanced Stock Management via PS_ADVANCED_STOCK_MANAGEMENT on 1.6 — note the old multi-warehouse ASM was removed in 1.7 and replaced by the simpler Stock page) plus tight back-office discipline buys you time — but it's a holding pattern, not a substitute for the snapshot-plus-local-decrement architecture above.

A few adjacent integrations people lump into "ERP"

"ERP" gets used loosely. Two jobs that often arrive in the same conversation are genuinely different problems with cleaner, off-the-shelf answers — don't build them into a bespoke ERP bridge:

  • Accounting and invoicing. Pushing orders and invoices into Xero, QuickBooks or your local accounting tool is a well-trodden path with dedicated connectors. We cover the patterns and the field-mapping gotchas in connecting PrestaShop to your accounting software — start there before you write a custom invoice exporter.
  • Transactional and notification email. If your integration needs to fire order or sync notifications reliably, that's a deliverability problem, not an ERP one. Get PrestaShop sending through a proper SMTP/transactional provider first — see email configuration in PrestaShop: SMTP, Gmail and transactional email — so your alerts actually land.

Treat the integration as infrastructure, not a deliverable

The sentence we tell every client at kickoff: an ERP integration isn't a project you finish, it's a thing you operate. Build the monitoring, the alerts, the dashboard and the "re-run last night's sync" button from day one. The teams that treat the integration as live infrastructure — with health checks and a visible sync log — stay calm. The ones who treat it as a finished deliverable get woken up at weekends.

Two practical foundations sit underneath all of this. Clean order data going in matters more than people expect: malformed or half-finished orders pollute the ERP and someone cleans them up by hand, so a tidy, reliable checkout (we build one in Checkout Revolution) pays for itself on the integration side. And the sync has to run without dragging the shop down — keeping the store responsive while batch jobs and webhooks fire is exactly the kind of thing Performance Revolution exists to surface, because a sync that quietly hammers your database at peak hours is a problem you want to see on a dashboard, not in a customer complaint.

Pick the ownership pattern first, match the mechanism to your stock velocity, make every operation idempotent and logged, and protect stock with snapshot-plus-local-decrement. Do those four things and the integration stops being a source of fire drills and becomes what it's supposed to be: the boring, reliable plumbing that lets the warehouse and the shop agree without anyone having to ask.

Share this post:
David Miller

David Miller

Over a decade of hands-on PrestaShop expertise. David builds high-performance e-commerce modules focused on SEO, checkout optimization, and store management. Passionate about clean code and measurable results.

Enjoyed this article?

Get our latest tips, guides and module updates delivered to your inbox.

Comments

No comments yet. Be the first!

Be the first to ask a question or share useful feedback.

Loading...
Back to top