Step-by-Step Stock Transfer Out Sync: Kingdee Cloud to MySQL in Practice
What This Strategy Solves
In one retail client we worked with, warehousing and finance live in two different systems: business documents flow through Kingdee Cloud, while analytics and downstream reporting sit in MySQL. The "deceptively simple" stock transfer-out document is exactly where things tend to break—organization fields in the source come back as localized composites, and if you don't normalize them at the integration layer, the numbers on both sides drift apart within three months, and you'll be digging through logs at 2 a.m. The goal here is to land Kingdee Cloud's step-by-step stock transfer-out documents into MySQL in a stable and traceable way, so downstream reports always reconcile with upstream documents.
Data Flow and Field Mapping
The end-to-end path is: Kingdee Cloud → Qeasy Data Integration Platform → MySQL. On the source side we use Kingdee's executeBillQuery to pull the transfer-out header and lines in one shot; on the target side we use batchexecute to write them into MySQL via SQL batching.
Key field mapping (excerpted from the source):
| Business meaning | Source field (Kingdee) | Target field (MySQL) | Notes |
|---|---|---|---|
| Document primary key | FSTKTRSOUTENTRY_FEntryID | id | Used as idempotency key |
| Document number | FBillNo | FBillNo | Business-visible number |
| Document status | FDocumentStatus | FDocumentStatus | String |
| Source organization | FStockOrgID.FName | FStockOrgID | Source has .FName suffix; target keeps code only |
| Business date | FDate | FDate | String |
| Document type | FBillTypeID | FBillTypeID | Type code |
Pay attention to how the source organization field is referenced: the metadata uses {{FStockOrgID.FName}}, while the target only receives the raw code. This single step is the linchpin of any organization-level reconciliation later, and it's also where the "centralized code mapping" pattern commonly used by Qeasy customers comes in.
How to Configure It in Qeasy
Source (Kingdee Cloud): choose API executeBillQuery, method POST, effect QUERY. Use FBillNo as the business number, and FSTKTRSOUTENTRY_FEntryID as the line id. There's no need to model header and body separately; keep autoFillResponse on and let it flatten the response into mappable fields.
Target (MySQL): choose API batchexecute, method SQL, effect EXUTE. Turn idCheck on for the id field so Qeasy de-duplicates by primary key before writing—never trust "the source won't repeat". number also points to id to make lookup by document number easy.
Mapping in the middle: build a single "field mapping view" in Qeasy and rewrite every source-side .FName-style reference into a plain code on the target. We strongly recommend maintaining a dedicated mapping table for master data (organizations, customers, warehouses) and referencing it from the main strategy—this is the "master data synced once, reused everywhere" pattern that Qeasy customers use heavily, so you don't rewrite mapping logic per document type.
Implementation Steps
Phased scheduling is what keeps this strategy stable. The two crontabs in the source material are worth a closer look:
- Source extraction:
*/7 * * * *, every 7 minutes. Near-real-time cadence, suitable for business hours. - Target write:
3-59/7 * * * *, offset by 3 minutes from the source to avoid both ends hitting the same batch at the same second.
A pragmatic schedule plan:
- Incremental starting point: on go-live day, run one full load to backfill historical transfer-out documents. For the incremental watermark, use a combination of
FDate + FBillNoinstead of a bare timestamp, to avoid missing documents. - Full load trigger: only run full loads during initialization and recovery. In Qeasy, model it as a separate "full strategy" that you trigger manually or schedule during off-peak hours.
- Scheduling frequency: keep 7-minute intervals during business hours (typically 08:00–22:00); drop to 30 minutes or stop outside business hours to save resources and reduce log noise.
The "dual-track incremental + full" pattern is another common Qeasy customer approach: incremental for everyday freshness, full load on demand whenever reconciliation goes off.
Pitfalls and Lessons Learned
- Don't pass
.FNamethrough directly.FStockOrgID.FNameis a display name; in multi-language environments or after an organization rename, historical values drift. The safe path is to pull an org-code mapping and join by code at the integration layer. - Stage header and body separately. If the transfer-out has many lines, a single query response can get bulky. A robust pattern—widely used by Qeasy customers—is to land the header first, then backfill the body by line key. This keeps blast radius small on partial failures.
- Always declare an idempotency key explicitly. Turn
idCheckon the target and de-duplicate byFSTKTRSOUTENTRY_FEntryID. Don't trust "the upstream won't repeat"—network jitter plus retries will eventually produce duplicates. - Don't store status codes raw. Kingdee's
FDocumentStatusis a code like "A"/"B". In MySQL, add astatus_namecolumn for the human-readable label, otherwise downstream report builders won't understand it. - The 3-minute crontab offset is not a coincidence. There's network and transaction latency between source commit and target visibility. Running both ends on the same
*/7will, in some cycles, read data that is "just committed but not yet landed." A few minutes of offset dramatically improves stability.
When This Fits (and When It Doesn't)
Fit: step-by-step transfer-out and other outbound documents; near-real-time sync into an analytics or downstream operational database; source is Kingdee Cloud with header and body returned in one interface. Doesn't fit: scenarios requiring strong transactional consistency (e.g., "transfer triggers immediate inventory deduction"), extremely large document volumes that need sharding, or cases without a stable primary key.