Production Warehousing Sync in Practice: Field Mapping and Pitfalls from MySQL to Kingdee Cloud
What This Strategy Solves (Scenario and Value)
Pushing production warehousing orders from a MES/WMS-style source system into an ERP is a critical step in supply chain integration. A common pain point we have seen on customer sites: once the WMS confirms an inbound production order, it must appear in Kingdee Cloud almost immediately, because that single record feeds cost accounting and downstream AR/AP. If sync is delayed or bill numbers collide, the finance team can easily end up reconciling discrepancies worth millions. This strategy does exactly one thing: reliably writes production warehousing records from MySQL into Kingdee Cloud in an incremental way, while keeping three categories of mapping rules — organization codes, dates, and bill numbers — centrally maintained so ongoing maintenance costs stay low.
Data Flow and Field Mapping (Source → Middle Layer → Target)
The MySQL source pulls a view through SQL; the Kingdee Cloud target writes through the batchSave API. The key field mapping is summarized below:
| Source (MySQL) | Middle-Layer Variable | Target (Kingdee Cloud) | Notes |
|---|---|---|---|
| CONCAT('RKD', id) | 入库单号 (Bill No.) | FBillNo | Prefix built at source; passed straight to target |
| 生产订单号 (HJ/HJGD prefix) | 生产订单号 | FPrdOrderNo (entry) | Prefix varies by supply org |
| DATE_FORMAT(update_time) | 日期 (Date) | FDate | String in YYYY-MM-DD |
| part_no | 成品编号 | FMaterialId (entry) | Material master must be synced in advance |
| confirm_numb | 入库数量 | FQty (entry) | 6 decimal places |
| m.delivery_org | 供应组织 | FPrdOrgId / FStockOrgId | Conditional mapping via _function |
| Constant 0.000001 | 工时 (Hours) | FLabourHours (entry) | Placeholder; later overwritten by process feedback |
For FPrdOrgId and FStockOrgId, we use a pattern that comes up again and again on Qeasy projects: a centralized code-mapping table. Through a _function expression, source T01.01 maps to target T01.06, and T04 maps to T04. Adding a new organization later is a one-line change rather than a redeploy.
How to Configure It on Qeasy
On the Qeasy Data Integration Platform, this strategy is modeled as a single A→B sync task.
- Source metadata: type
SQL, apiselect, primary parametermain_paramscarrieslimit/offset. The main query uses:created_atplaceholders, which the platform replaces with the current scheduling window at run time.autoFillResponse=true, so field labels become response fields automatically. - Target metadata: type
EXECUTE, methodPOST, apibatchSave,idCheck=trueso that FBillNo is used for deduplication and reruns do not create dirty data. - Scheduling: source crontab is
*/3 * * * *, target crontab is*/4 * * * *. Source runs one beat faster than target so upstream data is ready before the write attempt. - Runtime parameters: a
sys_configrow withconfig_id=351holds the incremental starting date, so you can re-trigger historical data without editing the SQL.
Implementation Steps (Phased Scheduling)
- Initialize environment and code mapping: in Qeasy's code-mapping module, maintain the
delivery_org → FPrdOrgId/FStockOrgIdtable. Every strategy that depends on this mapping reuses the same record so the rules do not scatter across SQL files. - Set the incremental starting point: for the first run, set
config_keyofconfig_id=351to the go-live date and leaveconfig_valueempty — the platform will pull records withupdate_time > go-live date. On the go-live day, setconfig_valueto the go-live date. TheCASE WHENblock in the SQL will then backfill the date automatically and prevent missing records. - Trigger full sync: temporarily enlarge
limit/offsetto cover the entire table and run a one-shot historical backfill. After it finishes, immediately restore the incremental configuration. We once hit an OOM here because the full load was not paginated; the safe approach is 5,000 rows per batch. - Enable scheduled runs: source polls every 3 minutes, target writes every 4 minutes. Under Qeasy's "header-then-entries phased" mode, the header is written first to obtain the inner bill id, then the entries are written back with that id.
- Monitoring and reconciliation: wire "source row count vs. target write count vs. failed retries" into the Qeasy dashboard and alert on any threshold breach.
Pitfalls and Lessons Learned
- Hard-coding organization codes in SQL means a redeploy per change. This is a classic anti-pattern. Lifting all organization mapping into Qeasy's centralized code-mapping module cut new-organization rollout time to about five minutes.
- Source builds the prefix, target builds it again, and the numbers collide. Source builds
CONCAT('RKD', id)and passes the result directly as FBillNo. Do not let Kingdee Cloud's numbering rule overwrite it, otherwiseidCheckwill reject duplicate numbers. - Hard-coding the incremental starting point as
2025-01-01. We once ran for six months without noticing that old data was being re-pulled. The reliable pattern is asys_configrow plus a platform variable, shared across all strategies as a single watermark table. batchSaverolls back the entire batch on any entry failure. This is a Kingdee Cloud quirk: one bad row kills the whole batch. We turned on Qeasy's "per-row graceful degradation" mode: failed rows go to an error queue for later manual or automatic retry, and they no longer block the rest.- Date field type mismatch. Source is DATE, target expects STRING. Without
DATE_FORMAT, the value carries a time component and Kingdee Cloud rejects it with "invalid date format". Format every date in SQL toYYYY-MM-DD; do not leave type conversion to the target.
Suitable and Unsuitable Scenarios
Suitable: discrete manufacturers with multiple organizations and plants, where production warehousing must flow from MES/WMS into ERP in near real time, and organization codes need centralized maintenance. Unsuitable: flows that require complex approval routing, need entries split by process, or require strong validations on the Kingdee Cloud side (e.g., mandatory cost center). Those flows need pre-cleaning rather than a simple sync.