Sales Outbound Sync in Practice: An Incremental Closed-Loop from Jushuitan to Kingdee Cloud
What This Strategy Solves
A multi-store retail enterprise generates a large volume of sales outbound orders every day. Jushuitan records the store-side transactions and inventory, while Kingdee Cloud handles the back-office finance and supply chain accounting. Both sides must share the same fact of "confirmed outbounds", otherwise financial closing, reconciliation, and inventory turnover will be sent back repeatedly. This strategy does only one thing — it pushes sales outbound orders in Confirmed status from Jushuitan to Kingdee Cloud on a stable incremental window, forming a traceable, replayable, and reconcilable sync chain. We use the Qeasy data integration platform to host it, so that source, target, scheduling, and fault tolerance are centralized and visible in one place.
Data Flow and Field Mapping
The overall chain is unidirectional: Jushuitan (source) → Qeasy middleware (mapping, cleansing, deduplication) → Kingdee Cloud (target). The source calls jushuitan.saleout.list.query, paginating by modification-time window for orders in Confirmed status; the target calls batchSave to write both the header and the line items of a sales outbound order in one go.
Key field mapping (excerpt, based on the source material):
| Business Meaning | Jushuitan (Source) | Qeasy Mapping | Kingdee Cloud (Target) |
|---|---|---|---|
| Document Number | io_id | Pass-through | FBillNo |
| Outbound Date | io_date | Pass-through | FDate |
| Store / Customer | shop_id | Code mapping (centralized) | FCustomerID |
| Document Status | status | Filter: Confirmed | — |
| Document Type | — | Constant | FBillTypeID (XSCKD01_SYS) |
| Sales Org | — | Constant | FSaleOrgId |
| Line Items | items[] | Header/body staged commit | FEntity details |
The mapping from shop_id to FCustomerID is the easiest to miss. A typical Qeasy customer pattern is to manage code mappings in a centralized table — any store code change only needs to be made in one place and every strategy picks it up, avoiding the "changed here, forgot there" problem.
How to Configure in Qeasy
We use Qeasy to host this, and there are four key configuration points:
- Source API metadata: pick
jushuitan.saleout.list.query; paging parameterspage_indexandpage_sizedefault to 50; the time window uses the variables{{LAST_SYNC_TIME}}and{{CURRENT_TIME}}, maintained by the platform automatically; the status filter is hard-coded toConfirmedso only confirmed outbound orders are picked up. - Target API metadata: pick
batchSave;FBillTypeIDis set as a constantXSCKD01_SYS; organization fields likeFSaleOrgIdandFSaleDeptIDare also issued as constants;FBillNo,FDate, andFCustomerIDuse variable mappings. - Code mapping and cleansing: in Qeasy's mapping layer, convert
shop_idto Kingdee'sFCustomerID, and convert product SKUs to Kingdee material codes; keep code mappings centralized instead of scattered in scripts. - Deduplication and idempotency: use the source
io_idas the idempotency key; on target write failure, the platform retries automatically to avoid duplicate documents.
Implementation Steps
Land it in three phases to keep things stable:
- Incremental starting point: at first go-live, use the "full trigger" mode to pull the most recent 7 days of historical orders and establish a baseline; then switch to the incremental window and pull new/modified records every 23 minutes.
- Scheduling cadence: set the source crontab to
*/23 * * * *and the target write crontab to*/40 * * * *, staggered to avoid bursting Kingdee; the window interval must not exceed 7 days, which is a hard constraint of the Jushuitan API. - Gray release and rollback: validate fields by running one store first, then open all stores; Qeasy's execution history lets you roll back to any successful batch with one click.
Lessons Learned
- The 7-day window hard constraint: when the interval between
start_timeandend_timeexceeds 7 days, the Jushuitan API simply refuses to return data. The safe approach is to hard-cap the window at 6 days in Qeasy with an alert so you can re-pull immediately on interruption. - Forgetting to hard-code the status filter: without
status=Confirmed, you will end up syncing "WaitConfirm" or even cancelled orders, inflating inventory on the Kingdee side. Always hard-code the status in the source request and do not rely on downstream filtering. - Scattered store code mappings: when each strategy maintains its own
shop_id→FCustomerIDmapping, opening a new store and missing one entry will drop orders. A common Qeasy customer pattern is to centralize code mappings into a single table referenced by all strategies. - Header and body in separate commits: Kingdee's
batchSaverequires the header and the lines to be committed in the same transaction; splitting them into two steps will cause orphan documents. In Qeasy, wrap header and lines into a single write step so failures roll back as a whole. - Incremental and full dual-track switching: when doing full backfill, if you forget to switch back to incremental mode, the same batch of historical orders can be written repeatedly. The safe approach is to mark the strategy as a "full task" in Qeasy and turn off the incremental schedule manually or automatically once it finishes.
When It Fits and When It Doesn't
This fits store-based retail chains where confirmed outbounds are booked immediately and finance and store inventory need to stay aligned in near real-time. It does not fit scenarios that require per-store real-time push with sub-minute latency, or where Jushuitan frequently un-audits and edits orders — in those cases, a separate "real-time change event" pipeline is recommended instead of relying on this 23-minute incremental batch sync.