Syncing Kingdee Payment Application to DingTalk Supplier Monthly Settlement Approval: A Qeasy Single-Strategy Tutorial
What This Strategy Solves
In one manufacturing client scenario, after finance approved a supplier monthly-settlement payment application in Kingdee Cloud, they still had to manually re-create the same approval in DingTalk — retyping the amount, supplier, and bank details for every document. At month-end, with over 200 documents, this ran well past midnight, and a single typo triggered a full rejection and re-submission. The goal of this strategy is to make the "Kingdee approved → DingTalk approval initiated" path fully automatic, eliminating manual transcription and latency.
Data Flow and Field Mapping
The flow is unidirectional: Kingdee Cloud CN_PAYAPPLY (payment application) → Qeasy Data Integration Platform → DingTalk topapi/processinstance/create (OA approval creation). Kingdee is polled via executeBillQuery, the middle layer transforms the payload, and DingTalk receives a complete approval-instance request body.
Key field mapping (DIRECT = direct passthrough, CONSTANT = constant, COLLECTION = lookup):
| Target Field (DingTalk) | Source Field (Kingdee) | Mapping Type | Notes |
|---|---|---|---|
| process_code | PROC-22EDF4E6-5CC9-4712-B9A3-34AEAF37B8AC | CONSTANT | Supplier monthly settlement process code |
| originator_user_id | F_VAOJ_FQR (originator name) | COLLECTION | Lookup "DingTalk Directory → Kingdee Employee" hub by name |
| dept_id | F_VAOJ_FQR (originator name) | COLLECTION | Same hub, return leader_in_dept.0.dept_id |
| Document number | FBillNo | DIRECT | Kingdee bill number |
| Counterparty / Supplier | FCONTACTUNIT.fname | DIRECT | Take name attribute |
| Apply amount | FAPPLYAMOUNTFOR_H | DIRECT | Header apply amount (book currency) |
| Payable amount | FPAYAMOUNTFOR_H | DIRECT | Header payable amount |
| Apply date | FDATE | DIRECT | Business date |
| Expected pay date | FEXPECTPAYDATE | DIRECT | Expected payment date |
| Due date | FENDDATE | DIRECT | Application deadline |
| Settlement org | FSETTLEORGID.fname | DIRECT | Base data, take name |
| Pay org | FPAYORGID.fnumber | DIRECT | Base data, take code |
| Apply org | FAPPLYORGID.fnumber | DIRECT | Base data, take code |
| Settlement currency | FSETTLECUR.Fnumber | DIRECT | Base data, take code |
| Bill type | FBILLTYPEID.fnumber | DIRECT | Base data, take code |
| Remarks | FDescription / F_VAOJ_Remarks | DIRECT | Remarks or extended field |
| Payment category | F_VAOJ_HKSX | DIRECT | Extended field |
| Source bill no | FSRCBILLNO | DIRECT | Upstream document number |
| Payee account name | FEACHCCOUNTNAME | DIRECT | Receiver account name |
| Payee bank name | FEACHBANKNAME | DIRECT | Receiver bank name |
| Payee bank account | FEACHBANKACCOUNT | DIRECT | Receiver bank account |
Kingdee Cloud base-data fields require a sub-property (e.g. .fname for name, .fnumber for code, Fnumber for currency code). This is the most common pitfall for newcomers and is reiterated below.
How to Configure on Qeasy
In on-site deployments we follow this pattern: first wire both endpoints into Qeasy's "Data Sources" — Kingdee Cloud needs the tenant authorization and the executeBillQuery FormId (CN_PAYAPPLY); DingTalk needs the open-platform AppKey/AppSecret and topapi/processinstance/create.
Then we build the strategy canvas in three layers:
- Source pull: FilterString set to
FApproveDate>='{{LAST_SYNC_TIME|dateTime}}', incremental by approval date; schedule at*/7 9-22 * * *. - Middle-layer mapping: Map Kingdee header fields one-to-one to the DingTalk approval-initiation parameters. Among the four top-level DingTalk params,
process_codeis a constant;originator_user_idanddept_iduse_findCollectionagainst the "DingTalk Directory → Kingdee Employee" hub;form_component_valuesis configured control-by-control in the platform UI. - Target push: Request body uses EXECUTE POST, schedule at
*/5 9-22 * * *so the queue drains slightly faster than it fills.
For code mappings, a common Qeasy customer pattern is "centralized code-mapping management" — all user_id/dept_id lookups are consolidated into the "DingTalk Directory → Kingdee Employee" hub strategy. The current strategy only references that hub rather than duplicating pulls, so when upstream directory data changes, all downstream consumers stay in sync without editing many strategies.
Implementation Steps
Our typical on-site rollout runs in three phases:
Phase 1: Incremental start point confirmation. Before first launch, pin down what LAST_SYNC_TIME should start from. The usual approach is to query one recently approved payment application in Kingdee and use its FApproveDate as the start, to avoid pulling all historical data at once. We recommend hard-coding this timestamp as a strategy variable for "cold start", then switching to normal incremental after verification.
Phase 2: Full-volume validation. Once cold start succeeds and the middle-layer mapping looks clean, run a separate full-volume pass — change FilterString to a time window or empty (subject to Kingdee API support) to pull a batch of historical data and push to DingTalk. This step mainly validates lookup hit rates at scale, whether bank-account fields are truncated, and whether amount precision is preserved.
Phase 3: Staged scheduling go-live. Source polls at */7 9-22 * * *, target pushes at */5 9-22 * * *, offset so they don't run "pull-while-push" concurrently. Another common Qeasy customer pattern is "header and detail in stages" — this strategy only pushes the header (since DingTalk's monthly-settlement approval is itself a header-only document). If we later need to extend to detail line items, we open a second strategy instead of overloading the first, which avoids breaking the header path.
Once steady, switch to a "dual-track incremental + full-volume" mode: incremental every 7 minutes for daily traffic, plus a full-volume reconciliation at 1 AM on the 1st of each month to catch any missed records.
Lessons from the Trenches
- Forgetting the sub-property on base-data fields. The classic mistake is passing the entire
FSETTLEORGIDobject straight into a DingTalk field, leaving DingTalk with a JSON string it cannot render. The safe approach: for any Kingdee base-data field, always take.fnameor.fnumber— whichever matches whether the DingTalk control is a text input or a dropdown. _findCollectiononleader_in_deptwith no fallback. When a DingTalk user has no department assigned, or their primary department is the root,leader_in_dept.0.dept_idcan be null or missing, and the DingTalk API rejects outright. This is where things break — we typically add a fallback in the transform layer: if the value is empty, pass-1(DingTalk's conventional root-department ID), so the document does not stall at the originator lookup.- Wrong field chosen as the incremental anchor. Using
FDATE(business date) is incorrect because business date can be earlier than approval date and is mutable when documents are un-approved then re-approved. UseFApproveDate, and strictly use "greater than" (not "greater than or equal to") the last sync time — otherwise boundary records at the same instant get silently dropped. form_component_valuescontrol names misaligned. The DingTalk approval-template controlnameis fixed by the template itself — what Kingdee calls "Apply Amount" may be labeled "Payment Amount" in DingTalk. Misalign one and the entire approval form renders with empty fields. Before configuration we always export the DingTalk template's control JSON and match every name one by one.
Suitable and Unsuitable Scenarios
Suitable: when Kingdee Cloud payment applications are the sole approval source, you want DingTalk OA to run monthly-settlement approvals, and DingTalk only needs header-level data. Not suitable: when DingTalk needs to maintain detail line items (e.g. multiple payment entries merged) or when DingTalk's approval controls cannot be mapped one-to-one against Kingdee fields — in the latter case we recommend a "master-data pre-generation + approval trigger" multi-strategy combination instead.