Syncing Kingdee Transfer Requisition to MySQL: A Flat-Table Incremental Integration Playbook
What This Strategy Solves
A retail chain running multi-warehouse transfers needs Kingdee Cloud transfer requisitions (STK_TRANSFERAPPLY) pushed into a MySQL data hub for scheduling and analytics. The catch: the source uses a header-line (master-detail) structure, while the target prefers a flat single table for analytical queries, and many fields are wrapped as base-data objects (FNumber/FName). We handle this on the Qeasy data integration platform with a "flat source pull + target single-table REPLACE INTO" pattern, ensuring encoding consistency and incremental stability.
Data Flow and Field Mapping
Data flow: Kingdee Cloud (executeBillQuery, FormId=STK_TRANSFERAPPLY) → Qeasy intermediate layer → MySQL single table tp_jd_transpferapply.
The source executeBillQuery returns a flattened result where each record represents one requisition line, and header fields are repeated on every line. The target stores a flat single table without master-detail separation, using FEntity_FEntryID (mapped to target FEntryID) as the deduplication primary key.
Key field mappings (excerpt; refer to source metadata for the complete list):
| Source Field | Target Field | Type | Description |
|---|---|---|---|
| FEntity_FEntryID | FEntryID | DIRECT | Line ID, upsert primary key |
| FBillNo | FBillNo | DIRECT | Document number |
| FTransferDirect | FTransferDirect | DIRECT | Transfer direction |
| FAPPORGID.FNumber | FAPPORG_FNumber | TRANSFORM | Application org code |
| FAPPORGID.FName | FAPPORG_FName | TRANSFORM | Application org name |
| FMATERIALID.FNumber | FMATERIALID | TRANSFORM | Material code |
| FUNITID.FName | FUNITID | TRANSFORM | UOM name |
| FQty | FQty | DIRECT | Requested quantity |
| F_PBLH_Base_WDTOUT.FNumber | F_PBLH_Base_WDTOUT_FNumber | TRANSFORM | WDT outbound warehouse code |
| F_PBLH_Base_WDTIN.FNumber | F_PBLH_Base_WDTIN_FNumber | TRANSFORM | WDT inbound warehouse code |
| FModifyDate | FModifyDate | DIRECT | Incremental cursor |
Object fields are always read as Object.FNumber for codes and Object.FName for display names. Centralized encoding mapping is one of the most common patterns seen among Qeasy customers.
How to Configure on Qeasy
Create a new strategy on Qeasy named "Kingdee Transfer Requisition → MySQL" with the following key points:
- Source connection: Kingdee Cloud WebAPI. Choose the
executeBillQueryinterface with FormId=STK_TRANSFERAPPLY, POST method, pagination Limit=100 / StartRow=0. - Pull filter: Set
FilterStringtoFModifyDate>='{{LAST_SYNC_TIME|datetime}}'for incremental pulls by last modified date. - Field mapping: In Qeasy's mapping panel, read object fields as
{{FMATERIALID.FNumber}}for codes or names; pass scalar fields directly via{{FBillNo}}. - Target connection: MySQL, choose API
batchexecute, method SQL. Target tabletp_jd_transpferapply, primary keyid, enable idCheck. - Write mode: Use
REPLACE INTOwithFEntity_FEntryIDfor deduplication; per-queue write batch limit 200. - Scheduling: Source crontab
*/8 * * * *, target crontab3-59/8 * * * *. Stagger writes by 3 minutes so the read pipeline never collides with the write pipeline.
No extra scripts are needed; the source returns a flat shape and the target simply passes fields through.
Implementation Steps
Roll out the strategy in three phases for stability:
- Phase 1: Incremental starting point. Before the first run, lock in the initial
LAST_SYNC_TIMEon the Kingdee side, ideally back to the business go-live date. Qeasy's first pull usesFModifyDate>='initial_time'to fetch everything. - Phase 2: Full-volume trigger. Manually trigger a full reload, then verify that the row count in
tp_jd_transpferapplymatches the line count ofSTK_TRANSFERAPPLYin Kingdee. Sample 3–5FBillNovalues and compare header fields against line fields. - Phase 3: Scheduling cadence. Once verified, enable the 8-minute incremental rhythm. Many Qeasy customers run an "incremental + full dual-track" pattern: 8-minute increments by
FModifyDateday-to-day, plus a weekly or monthly full reload as a safety net against extreme data loss.
Lessons from the Trenches
- Wrong object-field level. A typical mistake is writing
{{FMATERIALID}}, which returns the JSON object instead of the code. This is where things go wrong. Always write{{FMATERIALID.FNumber}}and store only the code string in the target. - Storing only FNumber or only FName. Reporting needs names for filtering, but codes are the join key. The safe practice is to store both
FXXX_FNumberandFXXX_FNameso you don't need to query Kingdee again. - Schedule collision. Source
*/8and target*/8firing at the same time causes instant queue congestion. Use target3-59/8to stagger 3 minutes behind the source for smoother throughput. REPLACE INTOside effects. In MySQL,REPLACE INTOis a delete-then-insert, which can shift auto-increment IDs and rewrite foreign keys. In projects that separate header and line phases, if the target table has foreign-key references, preferINSERT ... ON DUPLICATE KEY UPDATE; only useREPLACE INTOwhen you have confirmed there are no foreign keys.- Extension field naming inconsistency. Extension fields like
F_PRSH_Base_83g1andF_PRSH_Base_qtrcan carry different semantics across tenants. Confirm the meaning on the Kingdee side before deployment; never guess from the field name alone.
When to Use and When Not to Use
Use when: transfer requisitions must drive cross-warehouse scheduling analytics, feed downstream BI or e-commerce hubs, or the target requires a flat table for analytical queries while the source is header-line structured.
Do not use when: the target table already has foreign-key references sensitive to auto-increment changes, or the source documents must preserve master-detail structure for strict reverse lookup. In those cases, prefer a header + line dual-table model over a flat single table.