Master Data Sync from Kingdee Cloud to JKY: A Single-Strategy Tutorial
What This Strategy Solves
Material master data is the foundation of any supply chain. A retail enterprise uses Kingdee Cloud for finance and planning, while its front-end business runs on JKY. Both systems need to maintain the same item file — once codes or names drift apart, downstream orders, inventory, and reconciliation all break. This strategy does one thing: push approved materials from Kingdee Cloud to JKY's SKU file incrementally, keeping master data consistent across the chain.
Data Flow and Field Mapping
The flow is unidirectional: Kingdee Cloud (source) → JKY (target). No intermediate storage — it streams through Qeasy's integration pipeline.
The source object is Kingdee's material master form BD_MATERIAL, fetched via executeBillQuery. The target is JKY's SKU file, written in batches through erp.goods.skuimportbatch.
Key field mapping:
| Target (JKY) | Source (Kingdee) | Mapping Type | Notes |
|---|---|---|---|
| goodsName | FName | DIRECT | Item name |
| goodsNo | FNumber | DIRECT | Item code (business key) |
| outSkuCode | FNumber | DIRECT | External code, same as material code |
| unitName | FSaleUnitId | DIRECT | Sales unit (watch for nested structure) |
| skuName | FSpecification | DIRECT | Specification |
| skuBarcode | FNumber | DIRECT | Currently uses material code as placeholder |
| isBatchManagement | Constant 0 | CONSTANT | Defaults to No, extensible |
| cateCode / cateName | empty | CONSTANT | Left empty, extend later via collection mapping |
Source fields like FMATERIALID, FBARCODE, FCategoryID, FExpPeriod are fetched but unmapped — kept in Qeasy for future activation as needed.
How to Configure in Qeasy
In Qeasy, the strategy splits into two nodes: source QUERY + target EXECUTE.
Source configuration:
- api:
executeBillQuery, FormId hard-coded asBD_MATERIAL - FieldKeys: select only the fields used this run (FNumber, FName, FSpecification, FSaleUnitId) to slim down responses
- number and id both set to FNumber, idCheck: false (source does not enforce check)
- FilterString:
FApproveDate>='{{LAST_SYNC_TIME|datetime}}'— this is the core of incremental sync - Limit: 2000, StartRow uses platform variable
{{PAGINATION_START_ROW}}for auto pagination
Target configuration:
- api:
erp.goods.skuimportbatch, method POST - idCheck: true, so the target decides insert vs update based on goodsNo / outSkuCode
- Field mapping per the table above, constants written directly
Centralized code mapping: Qeasy supports placing cross-strategy code lookups (such as the future FCategoryID → cateCode mapping) in a hub or dedicated strategy, accessed via _findCollection. This is a common pattern — keeping encoding rules in one place avoids the trap of fixing one strategy and forgetting another.
Implementation Steps
Three phases.
Phase 1 — initialize the incremental anchor. For the first run, do a full historical pull: set LAST_SYNC_TIME to a very early date (e.g. 2000-01-01) so the platform pushes every approved material to JKY. After this run, LAST_SYNC_TIME is automatically updated to the current sync timestamp.
Phase 2 — full trigger and validation. Once the full sync completes, manually spot-check that codes, names and specs match on both sides. Pay special attention to two cases: unit fields that look wrong (when FSaleUnitId returns nested objects, unitName is written as-is), and SKUs whose barcode is a material code placeholder.
Phase 3 — steady-state scheduling. Set the crontab to */15 7-23 * * *, running every 15 minutes during business hours. Qeasy's scheduler maintains LAST_SYNC_TIME automatically — no manual intervention needed.
Dual-track full + incremental: another common Qeasy customer pattern — full sync only runs manually during initialization or data repair; day-to-day relies entirely on incremental, which is both stable and fast.
Lessons Learned
First, nested structures on unit fields. FSaleUnitId in Kingdee usually returns a nested object. If you write {{FSaleUnitId}} directly, the target receives a JSON string and the unit name becomes something like [object Object]. The safe move is to expand it in Qeasy — write {{FSaleUnitId.FName}} — or use a COLLECTION lookup against the unit table.
Second, the barcode placeholder risk. The current skuBarcode uses {{FNumber}} as a placeholder. Once the business side finds scanned codes are not real barcodes, troubleshooting becomes painful. The recommendation: switch skuBarcode to {{FBARCODE}} from the first deployment, and use _function to handle nulls — fall back to the material code when the barcode is empty.
Third, incremental anchor rollback. If someone accidentally rewinds LAST_SYNC_TIME to a much earlier date, the system triggers a full re-push, and the target may reject duplicates. In Qeasy, enable idCheck on the target and watch alert logs right after the first push.
Fourth, FCategoryID left empty as an extension point. The source material leaves cateCode and cateName as empty constants — a "get it running first, fill in later" compromise. When extending to category mapping, always use COLLECTION rather than hard-coding, or categories will desync the moment they change.
Fifth, scheduling window trade-offs. */15 7-23 * * * is a rule of thumb. Material approvals are rare at night, so narrowing to 7–23 reduces wasted calls. But if the customer has cross-time-zone night shifts, extend the window to 24 hours.
When to Use and When Not to Use
Use when: there is one ERP source of truth for master data, the target needs to mirror the material file, and incremental sync on approval date covers all changes. Do not use when: bidirectional material sync is required, related master data (supplier, customer) must be joined and pushed together, or the target carries many manually maintained local fields that need a dedicated conflict-merge strategy.