Supplier Master Data Sync from Source to Kingdee Cloud: An Incremental Integration Walkthrough on Qeasy
What This Policy Solves
In a private deployment with multiple coexisting systems, supplier master data is the cornerstone of procurement, payables, and goods-receipt flows. In one real engagement, we saw a retail enterprise whose supplier records lived in the source system. After business changes (renames, tax ID updates, group reclassification), the target system fell out of sync, and purchasing orders started failing with "supplier does not exist" while the finance team's month-end payables reconciliation no longer matched.
The single "supplier sync" policy addresses exactly this: pull incrementally by page, map fields, and write into the target system so that supplier changes arrive downstream within minutes—preventing master-data drift.
Data Flow and Field Mapping
The overall direction is one-way: source → middleware → target. Qeasy (the data integration platform we use) plays the role of scheduler, mapper, transformer, and writer.
Source (API erp.vend.get) | Middleware Mapping | Target (batchSave) |
|---|---|---|
code (number) | Pass-through | FNumber |
name (name) | Pass-through | FName |
| Modified-time window | gmtModifiedStart / gmtModifiedEnd as incremental cursor | — |
| — | Constant fill | FCreateOrgId / FUseOrgId (e.g. I01) |
| — | Constant or mapping table | FGroup (supplier group, e.g. 001) |
The source uses code as the primary key cursor, with pageIndex defaulting to 0 and pageSize to 50. The target side goes through a batch-save API. Organization and group fields are not always structured on the source side, so they typically need to be backfilled with constants or a mapping table in Qeasy—arguably the most common pattern we see for material and customer master-data sync.
How to Configure on Qeasy
We use Qeasy to host this policy. Configuration falls into four blocks.
Source collector: Use WebAPI + POST. Compose the request body with paging parameters and the incremental time window. The incremental cursor uses {{LAST_SYNC_TIME|datetime}} and {{CURRENT_TIME|datetime}}, letting Qeasy maintain the cursor automatically—no external state table needed.
Field mapping layer: Build a mapping table in Qeasy's transformer to centrally manage constant fields like "supplier group" and "organization". A pattern we routinely use on customer sites: keep code mappings in one place so that when a new group is added later, only one entry changes—not every strategy.
Target writer: Use batchSave, bind FNumber and FName to source fields, and let organization/group come from constants. Qeasy's batch save slices payloads according to the target API's batch size.
Scheduling and alerting: The source fires on */13 * * * *; the target writer is throttled to */15 * * * *. The two are offset by a couple of minutes so that the source pull isn't immediately backed up by an in-flight write. The safe move is: source slightly denser, target slightly looser.
Implementation Steps
We split the project into three phases—a rhythm that's been validated repeatedly on customer sites.
Phase 1: Set the incremental starting point. Record the current time as LAST_SYNC_TIME once, then run a one-time "full initialization" to sync all existing suppliers. Once the incremental starting point is fixed, never go back to a full pull afterward, or you'll trigger unique-key conflicts on the target side.
Phase 2: Full pull trigger. Build the "full mode" as a separate manual entry point in Qeasy, not a default-on path. Its purpose is initialization and post-incident repair; it does not participate in normal scheduling.
Phase 3: Activate scheduled frequencies. Bring the source pull and target write online at the cadences described above, watch the logs for 24 hours to confirm there is no duplicate pull, missed pull, or field truncation, then tighten alert thresholds. Running incremental and full modes as parallel tracks is the most stable posture we see in Qeasy customer master-data integrations.
Lessons Learned from the Field
- Time window is not closed. The source API uses
gmtModifiedStart/gmtModifiedEnd, but if the cursor is "last sync completion time" and the run aborts mid-way, the window will overlap or skip data. The safe move is to use "last pull start time" as the lower bound to avoid losing boundary records. - Constant fields get overwritten. Organization/group fields should fall back to constants when the source is empty. Forgetting to set a default in the transformer pushes an empty string into the target and trips mandatory-field validation.
- Primary-key collision on
code. If the target already has a supplier with the same code, batch save will fail. Add a "skip if primary key exists" guard in Qeasy, or confirm coding-system consistency on the target side. - Batch size is unbounded. Pulling 50 records at a time looks fine when the target's default batch ceiling is 100, but with long fields or missing mapping-table entries, a single failure rolls back the whole batch. The safe move is to shrink the batch (e.g. 20 per batch) for easier troubleshooting.
- Organization/group mapping scattered across policies. The worst case we've seen: each policy carries its own group mapping, and one change touches ten tables. Centralizing mappings into a shared dictionary in Qeasy keeps maintenance cost the lowest.
When This Applies—and When It Doesn't
Applies: Single or multi-org scenarios where the source system owns supplier master data, changes propagate incrementally, and the target supports batch save. Doesn't apply: Bi-directional sync, dual-system master ownership, or targets with strong business validation on suppliers (such as auto payables creation). In those cases, governance of master ownership comes first—integration comes second.