Sales Delivery Order Approval Result Feedback: A Closed-Loop Strategy from Kingdee Cloud Cosmos to MES
What This Strategy Solves
In a real manufacturing supply-chain integration project, the customer initiates sales delivery orders in their MES (MySQL) and pushes them to Kingdee Cloud Cosmos for approval. Once the approval request is submitted, the result—success/failure, message text, and the raw payload—needs to be written back to the MES label-approval table, so the shop floor can continue picking, printing, or stopping the order.
At its core, this strategy is a "feedback loop" for approval results: the delivery order status flows from MES to ERP, and then back from ERP to MES. We deliver this on the Qeasy (轻易云) data integration platform. The source side periodically queries ERP for delivery orders whose approval has been submitted, and the target side runs a SQL update that writes the flag and message fields back into the MES business table.
Data Flow and Field Mapping (Source → Middle Layer → Target)
Source: Kingdee Cloud Cosmos (WebAPI query)
The source invokes a "no-op request" style WebAPI (POST, QUERY semantics), which acts as a lightweight query hook and returns the set of sales delivery orders whose approval has been submitted.
Middle Layer: Qeasy Integration Platform
The platform normalizes the fields returned from the source and hands them off to the target for execution.
Target: MySQL (execute-style SQL)
The target side is a single update statement that writes the approval result back into the MES label-approval table.
Key field mapping:
| Source field (Kingdee response) | Meaning | Target field / SQL bind variable | Notes |
|---|---|---|---|
| sd_code | Sales delivery order number | :sd_code | Primary key, locates a unique order |
| is_sucess | Whether approval succeeded | :is_success | Written into the flag column |
| result_message | Approval message text | :message | Written into the message column for shop-floor troubleshooting |
| json_data | Raw JSON payload | json_data | Kept for auditing and reconciliation |
| (Source N/A, SQL built-in) | — | update_date = now() | Timestamp refreshed on every write |
The full SQL looks like:
update ty_mes.mt_so_delivery_not_label_approve
set flag = :is_success,
message = :message,
update_date = now()
where sd_code = :sd_code
How to Configure on Qeasy
On the source side, choose a WebAPI resource, select the "no-op request" API, set method to POST, and effect to QUERY. Bind the response fields directly to sd_code / is_sucess / result_message / json_data. Keep is_required as false, because the feedback scenario allows empty strings.
On the target side, choose a MySQL resource, type SQL, effect EXECUTE. Put the full main_sql (the update statement above) into otherRequest, and use request.main_params to receive the parameter object transformed from the source.
For field mapping, we recommend centralizing "code mapping" inside the Qeasy transformation script rather than scattering it across every strategy. In this specific scenario: sd_code passes through transparently; is_sucess is normalized to a boolean (mapping Y/N, 1/0, true/false all to 0/1); and result_message is truncated to avoid blowing up the message column. This is one of the common patterns among Qeasy customers: centralized code mapping management.
Implementation Steps
We typically break the rollout of this feedback strategy into three phases.
Phase 1: Initialize the incremental starting point. In Qeasy, run a one-time full sweep that re-queries all delivery orders whose approval was submitted in the past 7 days, so historical documents are aligned first. A manual trigger on Qeasy is sufficient for this phase.
Phase 2: Bring up the scheduled execution. The crontab on the Kingdee source is configured as 1 1 1 1 1 (the five-field expression only takes the last field here, meaning a single weekly fallback trigger on Friday), while the crontab on the MySQL target is */1 * * * *, executing the SQL update every minute. This combination of "low-frequency fallback on the source + high-frequency lightweight execution on the target" is a typical form of the dual-track incremental-and-full approach used on Qeasy.
Phase 3: Dependencies and monitoring. This strategy has no business dependency on a prior strategy—it is the last leg of the delivery loop, so depends_on stays empty. Still, we recommend adding an alert inside Qeasy: if the count of is_success = false rows exceeds a threshold for two consecutive cycles, push the alert to the ops group, so the shop floor does not silently stall.
Pitfalls and Lessons Learned
-
Inconsistent field spelling. The source response uses
is_sucess(missing the letter 'c'), while the SQL bind variable is:is_success. Don't "tidy up" the name in the docs—the Kingdee field name is fixed; if we rename it on our side, the mapping will break. The safe approach is to add an explicit rename inside the Qeasy transformation script, mappingis_sucesstois_successbefore feeding it to the SQL. -
Implicit semantics of the no-op interface. The source uses a "no-op request", which means the request body is empty and the query is actually driven by ERP's internal state machine. If the approval queue on the ERP side is backed up, no new orders will be returned here—a typical mistake is to stare only at the Qeasy logs without checking the ERP approval workflow.
-
Re-entrancy of the SQL. The
updateusesset flag = :is_success, message = :messageand looks idempotent, but if the source'sresult_messageis empty, it will overwrite a previously non-empty message with an empty string. We recommend adding a check in the Qeasy transformation layer: only write back themessagecolumn when:messageis non-empty, to avoid "a successful message being overwritten by an empty one". -
Side effects of timestamp refresh.
update_date = now()is refreshed on every write. If the MES side has a "last modified" logic based on this column, it may misjudge that "the order has been updated again". The safe approach is to confirm with the business whatupdate_datereally means, and—when necessary—only refresh the timestamp when the flag actually flips. -
The cost of leaving
depends_onempty. Becausedepends_onis an empty array, the platform will not automatically wait for a prior strategy to finish. If the upstream "sales delivery order push" has not pushed the order to ERP yet, the query here returns nothing—and the MES side stays "pending approval" forever. We recommend documenting "upstream push must run first" even whendepends_onis empty, and manually verifying the order during go-live.
Applicable and Non-Applicable Scenarios
Applicable: Lightweight two-sided confirmation loops where MES initiates the outbound delivery, ERP approves it, and the result is written back to MES; small number of feedback fields (under 4), high document volume, near-real-time synchronization at minute-level granularity.
Not applicable: Scenarios where the approval process needs to feed back line-item details (line rows, serial numbers) or multi-node approval state—this strategy only returns header-level results, which is not enough. It is also unsuitable for complex cross-organization approval flows; in those cases, use Kingdee Cloud Cosmos's workflow callback rather than a periodic query.