Return Document Two-Way Sync in Practice: Breaking Down the Strategy of Querying Returns from a Source ERP and Writing Back to a Service Desk on Qeasy
What This Strategy Solves (Scenario and Value)
On a real engagement with a retail company that had just upgraded its ERP, the after-sales team was still tracking return progress in a separate service desk system. Each system owned its own slice of the work: the ERP issued return documents, while the service desk tracked progress, chased logistics, and handled customer follow-ups. The leadership had a very down-to-earth ask—the moment the ERP produced a new return, a corresponding ticket should appear in the service desk automatically. No more manual re-keying.
The problem is that the source ERP exposes a returns API keyed on a modification-time window, while the target service desk accepts updates against an existing ticket ID. The translation between "pull a time window" and "locate a ticket and back-fill progress" is exactly what this strategy on the Qeasy data integration platform is designed to handle. Its value is not flashy—it is about semantic alignment between two heterogeneous systems, mapping ERP return fields into the statuses and notes the service desk can understand.
Data Flow and Field Mapping
The overall direction of the strategy is Source → Middle Layer → Target: pull return details from the source system, perform field conversion inside Qeasy, and write the result back to the target system.
The source side is a POST endpoint that accepts a time window or an online order number and returns paginated return records. The target side is another POST endpoint that accepts a ticket ID and a contents object—essentially an update-by-ticket operation.
| Semantic | Source (returns query) | Qeasy middle layer | Target (ticket update) |
|---|---|---|---|
| Window entry | modified_begin / modified_end | Injected by scheduler | — |
| Document number | tradeNo | Join key | — |
| Return note | Business field (custom) | substring_index('{{buyerMemo}}', ':', -1) extracts ticket ID | task_id |
| Progress / status | Return processing stage | Normalized mapping | contents (object) |
| Pagination | pageSize / pageNo | Automatically managed by Qeasy | — |
A note on that task_id: it is not a direct read of any source field. Instead, it is extracted from the last segment of the source system's buyer memo using substring_index(..., ':', -1). This is a very common pattern we encounter in customer engagements—there is no dedicated association table between the two systems, so a soft association is built on a convention embedded in a text field. It looks rough, but it works in practice.
How to Configure It in Qeasy
Inside Qeasy's strategy configuration page, pick the source ERP connector on the source side and the target service desk platform on the target side, and supply the platform identifier and credentials. In an on-premises deployment, traffic stays on the internal network and credentials never traverse the public internet.
Key points for the source metadata:
type=QUERY,effect=QUERY,method=POST.- Use
tradeNoas the business number field andtradeIdas the primary key, withidCheck=trueto ensure deduplication. - Set
autoFillResponse=trueso the response structure is auto-registered into Qeasy's data model. - Leave the time-window fields
modified_begin/modified_endempty for the scheduler to inject.
Key points for the target metadata:
type=WebAPI,effect=EXECUTE,method=POST.app_idandproject_idare required and have fixed values; write them in directly.- The
task_idis computed via the expression_function substring_index('{{buyerMemo}}', ':', -1), deriving the soft-associated ticket ID. contentsis an object type and is populated by the mapping rules.
Both buildModel flags are set to false on this strategy—the source side is read-only, and the target side updates existing tickets rather than creating new ones, so neither side needs a generated table.
Implementation Steps
We split the rollout into three phases, each visible to the customer's business side.
Phase 1: Align the incremental starting point. After deployment, reconcile by hand with the source system's last-modified time to determine an "incremental starting point." Hard-code this into the scheduler's initial parameters so the first run neither misses historical data nor re-pulls it.
Phase 2: Trigger a full-volume safety net. Once the starting point is agreed, manually trigger a full sync. The source endpoint restricts the window to seven days, so cut historical returns into seven-day slices and process them in multiple rounds. A common pattern we see in the field is "incremental during the day, full-volume safety net at night," giving you a two-track operation.
Phase 3: Scheduling frequency and time window. The crontab is set to */10 8-23 * * *, meaning a run every 10 minutes during working hours. This frequency comes from experience: faster than that and the source endpoint's pagination pressure spikes; slower and the "to-do" in the service desk lags visibly, and the after-sales team will come asking why nothing has shown up.
Lessons Learned From the Field
-
Boundary semantics differ between systems. Whether the modification-time window is half-open or fully closed is interpreted differently by the two systems. We worked with the customer's business side and compared actual source responses three times to settle this. The safe approach inside Qeasy is to add a one-second "overlap window" on the source side and deduplicate by primary key on the target side—it is better to query one extra second than to miss a record.
-
Soft-association fields broke overnight when copy was changed. Because
task_idis derived from a memo field, an operations copy change to the "return reason" wording wiped out everything after the:separator. In the post-mortem, we recommended the customer codify this convention in their operations SOP, and we added an exception fallback in Qeasy—when the ticket ID cannot be extracted, the original memo is written intocontentsfor traceability. -
Do not guess a pagination size. The source endpoint allows a fairly large
pageSize, but during a full-volume run a too-large page will blow up memory. Inside Qeasy, we cappedpageSizeat a moderate value and ran with a "time window first, pagination as fallback" pattern. It is far more stable. -
Do not skip
idCheck. TheidCheck=truesetting on the source side looks redundant, but it is the last line of defense for deduplication. If any upstream stage double-posts, the target ticket will be updated twice, and the business side will see the bizarre report of "I just updated this ticket and it was overwritten." -
Centralize encoding mappings. Return status and return reason are named differently in the two systems. Do not scatter these mappings across every strategy. In Qeasy we maintain a single central mapping table so that when a new return type is added later, only one place changes and all strategies pick it up.
When This Pattern Fits and When It Does Not
It fits when: the two systems have no existing direct connection, return volume is moderate but timeliness matters, and a soft association with minimal change to either side is acceptable. It does not fit when: return volume is large enough to require streaming, both systems already offer an official direct connector, or the customer explicitly rejects soft-association approaches that rely on convention strings embedded in text fields.