Sales Return Order Sync in Practice: Incremental and Idempotent Design from Wangdiantong to Yongyou BIP
What This Strategy Solves
In an omni-channel retail business, offline and e-commerce returns and exchanges are first recorded in the retail e-commerce platform (Wangdiantong·QiMen in this case), where they go through approval, refund, receipt, and settlement. But financial accounting, receivables offsetting, and inventory reconciliation all live in the large-scale ERP (Yongyou BIP). Without integration, the finance team ends up re-keying returns manually, inventory numbers diverge, and customer sub-ledgers do not match.
The goal is to push return orders that have already been settled (or partially settled) in Wangdiantong into Yongyou BIP as sales return vouchers, so that "once approved, it's in the books."
Data Flow and Field Mapping
The overall flow is: Wangdiantong·QiMen → Qeasy integration platform (middle layer) → Yongyou BIP sales return voucher.
| Dimension | Source (Wangdiantong) | Middle Layer (Qeasy) | Target (Yongyou BIP) |
|---|---|---|---|
| Document number | refund_no | refund_no | code / resubmitCheckKey |
| Unique key | refund_id | refund_id | id |
| Incremental window | Last modified time | LAST_SYNC_TIME → CURRENT_TIME | - |
| Sales org | shop_no | Look up mapping_sale_org | salesOrgId |
| Customer | shop_no | Look up mapping_customer | agentId |
| Transaction type | Fixed business meaning | Fixed value | transactionTypeId |
| Retail investor flag | Derived from source status | Rule field | retailInvestors |
Key point: shop_no is a store code on the source side, but on the target side it must be translated into two master keys: sales organisation and customer. Do not scatter this mapping across strategies. Maintain it centrally in a dedicated "store-to-customer/org mapping" strategy, and have all other strategies reference it via _findCollection. Change once, apply everywhere. This centralised mapping pattern is the most common practice among Qeasy customers.
How to Configure It in Qeasy
Source configuration (QUERY)
- Use the
wdt.refund.queryAPI, POST method. Start with the default page size of 40 and tune upward only after load testing. - Bind
start_time/end_timeto the platform variablesLAST_SYNC_TIMEandCURRENT_TIMEso nothing is missed. - Filter on
process_statusat the source side. Pull only orders that are clearly settled ("completed: 90", "partial received: 70/71"). Skip in-flight ones such as "pending review: 20" or "pending receipt: 60" so the finance system does not have to flip-flop on offsets. - Use
refund_id(orrefund_no) as thenumberfield and enableidCheck. The platform will deduplicate automatically.
Target configuration (EXECUTE)
- Use the
/yonbip/sd/vouchersalereturn/singleSaveAPI. The single-save variant fits non-standard documents generated by returns. - Generate
resubmitCheckKeyclient-side. In Qeasy, compose it as{{refund_no}}-4with a business suffix to keep it globally unique and human-readable. - For the
codefield, passrefund_nounconditionally. Whether Yongyou BIP actually uses it depends on the numbering rule set there (auto-numbering ignores it, manual numbering requires it). Passing it unconditionally is the safer choice. salesOrgIdandagentIdare resolved through_findCollectionlookups. If the lookup fails, short-circuit the whole document rather than writing a half-formed one into finance.
Implementation Steps
Phase 1: Baseline alignment (pre-go-live) Run a one-off full pull to backfill all already-settled returns. Confirm the mapping table has full coverage, then run a reconciliation pass on the target side.
Phase 2: Switch to incremental (go-live)
On the source side, switch start_time to LAST_SYNC_TIME and schedule every 10 minutes (1-59/10). On the target side, stagger to 5-59/10 so writes do not start before the source pull finishes. The dual-track pattern (full pull as a safety net, incremental as the steady state) is the most common go-live path used by Qeasy customers.
Phase 3: Exception monitoring (run)
Watch for three failure modes: 1) shop_no missing from the mapping table; 2) resubmitCheckKey collision (target-side retries polluting idempotency); 3) target returns success but no voucher is posted in finance (header saved, but the approval chain did not complete). Catch the first two in Qeasy alerts directly. The third requires a joint review with a reconciliation job.
Pitfalls and Lessons Learned
Pitfall 1: Incremental window drift. Wangdiantong filters on "last modified time." If customer service keeps editing a refund amount, the same refund_no will be pulled multiple times. Fix: use refund_id as the idempotency key, not refund_no.
Pitfall 2: Missing shop_no mapping. A new store opens, its shop_no is not yet registered in the mapping table, and source documents start arriving. The result is full-document create failures on the target side with no signal at the source. The symptom is inventory drift. Safest approach: alert on missing mappings, never default to an empty value.
Pitfall 3: Header passes, line items fail. Yongyou BIP's singleSave submits header and lines together. If one SKU in the lines is unknown on the target side, the entire document is rolled back, triggering retry storms. The common Qeasy pattern here is staged header/line processing: write the header first in a draft state with empty lines, then fill the lines, and retry only the lines on failure rather than the whole document.
Pitfall 4: Treating partial settlement as final. Statuses 70/71 (partial receipt) will continue to receive goods. If pushed to finance as final, the second settlement will create a "returned but still in transit" state mismatch. Either filter these out at the source, or push them to an intermediate "pending settlement" state on the target.
Pitfall 5: Non-unique resubmitCheckKey. Using refund_no directly as the idempotency key breaks if the source resets the refund_no sequence (year-end, migration). New and old documents may share a key, and the target will silently drop one of them. Always append a business suffix.
When It Fits, When It Doesn't
Fits: Multi-store, multi-sales-org retail and omni-channel businesses where the e-commerce ERP and the financial ERP are separate, and returns must enter financial accounting and inventory offsetting.
Does not fit: When the source and target are the same system (use the built-in document conversion instead of a middle layer), or when returns do not need to enter finance at all and only serve logistics and after-sales tracking (a ticketing system is lighter for that).