Pushing Sales Orders from CRM to ERP: A Complete Sync Walkthrough on a Single Strategy
What This Strategy Solves
Pushing sales orders from a CRM into an ERP sounds simple—"just move the order over." But three months in, totals drift, amounts cannot be reconciled, and order statuses fall out of sync. The warehouse has shipped, yet finance has no record. So this strategy is not really about "can we push it," but about "by what time window do we pull incrementally, when do we backfill, how do we align status fields, and how do we recover when things go wrong."
Data Flow and Field Mapping
The chain is: source (CRM sales orders) → middle layer (Qeasy data integration platform) → target (ERP sales orders). The source pulls orders by time window with pagination; the target writes idempotently by order number. The table below shows the key field mapping we actually use in real projects, named by business meaning rather than the raw field names on either side.
| Business Meaning | Source CRM Field | Target ERP Field | Handling Notes |
|---|---|---|---|
| Online order no. | order_no | so_id | Idempotency key—same number must be used on both sides for dedup |
| Order date | account_date | order_date | Mind time zones; normalize to YYYY-MM-DD in the middle layer |
| Buyer account | company_phone | shop_buyer_id | Mask sensitive data in the middle layer |
| Shop code | shop_code | shop_id | Mapping must exist on the target side or upload fails |
| Order status | order_status | shop_status | Enums differ across systems; a mapping table is typical |
| Line items | items[] | items[] | Header and body handled in separate stages |
How to Configure on Qeasy
When we use the Qeasy data integration platform to host this strategy, there are a few typical configuration steps.
Step 1: Source connection. Register the CRM order query interface as a data source. Use {{LAST_SYNC_TIME|datetime}} for start_time and {{CURRENT_TIME|datetime}} for end_time. Page through results with start_index and count, looping until an empty page is returned.
Step 2: Target write. Register the ERP order upload interface as an execute action. Maintain a centralized "shop code mapping table" on Qeasy so the mapping is not scattered across every strategy—this is one of the most common patterns among Qeasy customers: centralized code mapping management. Bind so_id directly to source order_no and enable idCheck to avoid duplicate pushes.
Step 3: Field mapping and transformation. In the mapping canvas, pair each field from the table above. Use a "lookup table" component for enum values like order status and shop code rather than writing scripts on every field.
Step 4: Model building. When the target is flagged buildModel=true, let Qeasy auto-build the model from the API response, avoiding manual modeling work.
Implementation Steps
We recommend rolling this strategy out in three stages.
Stage 1: Incremental starting point. Use the latest order number or most recent successful time as the initial LAST_SYNC_TIME, run a manual full backfill to populate shops, members, and product codes on the target. This step usually depends on prerequisite strategies such as products and members.
Stage 2: Full backfill trigger. Full backfill is not run every cycle—it is a safety net, e.g. once a month or manually when the incremental run fails and orders are suspected lost. This is another common pattern among Qeasy customers: incremental and full dual-track.
Step 3: Scheduling frequency. The source crontab should be 0-59/10 7-22 * * *—every 10 minutes from 07:00 to 22:00. The target should be staggered to 3-59/10 7-22 * * * so it does not start writing while the source is still pulling. Reserve the night for inventory and reporting jobs.
Lessons Learned
Pitfall 1: Hard-coding count. A typical mistake is fixing count at 10 or 50; with large customers pushing thousands of orders a day, this drops data. The safe approach is to validate the logic with the default of 10, then raise to a safe ceiling (e.g. 100), and explicitly check for empty pages in the loop.
Pitfall 2: Passing order status enums through unchanged. Names like "Paid" or "Pending Shipment" almost never match across systems, and direct passthrough will break. For every customer project we now maintain a status mapping table centralized in Qeasy's lookup table—business owners change it in one place.
Pitfall 3: Ignoring the removed parameter. If deleted or voided orders on the source are not pulled separately with removed=1, they are permanently lost. The safe approach is: pull normal orders incrementally + schedule a separate pull for deleted orders + run a void flow on the target.
Pitfall 4: Sending header and body together. The ERP API often requires the header first and then the body, or demands totals in the header. Sending the whole order in one call makes it impossible to tell whether a failure is in the header or the body. We standardized on header and body in separate stages—push the header first, then the lines; failures can then be retried.
Pitfall 5: Shop code not pre-created. When shop_id does not exist on the target, the call errors out—and since it is an optional field, it is easy to overlook. The safe approach is to add a "shop code existence check" in the strategy pre-validation on Qeasy.
When This Applies and When It Does Not
Applies: Retail or distribution companies where the CRM is the order entry point and the ERP is the post-fulfillment hub; daily order volumes from a few hundred to a few thousand, mostly incremental; the three code sets—order status, shop, buyer—need to be reconciled on both sides.
Does not apply: Real-time second-level sync required on both sides (Qeasy defaults to 10-minute scheduling); huge structural differences between orders requiring complex secondary development; the source is not the authoritative order source and there are still many manual order entry scenarios—in that case, fix the source first.