Published at: 2025-10-30

4.1 [Function Module] Pre-Sync, Sync, and Post-Sync Functions


1. Pre-Sync Custom Function Execution with Data Filtering

When the function returns a result containing "isExec": "false", the synchronization process will terminate, and no changes will be made to the target data.

Test Scenario:
Add "isExec": "false" or "isExec": false to the Map returned by the custom function. The target data will remain unchanged.

Function Code:
java Map map = ["details":syncArg.details, "objectData":syncArg.objectData, "isExec":"false"]; return map;


2. Custom Function Execution During Synchronization

After the function executes, the mapped values modified in the function will be updated, while the enterprise ID and object apiName remain unchanged.

Test Scenario:
- Only new data will include Sub-objects.
- During updates, mapped values for objects will be modified. Without specific Sub-object identification, updates will apply uniformly to both Primary and Sub-objects, renaming them according to the custom function’s assigned values.

Function Code:
java log.info("Event type: " + syncArg.destEventType); log.info(syncArg.objectData); log.info(syncArg.details); Map objectData = syncArg.objectData as Map; objectData.name = "Modified by Custom Function"; Map map = ["details":syncArg.details, "objectData":syncArg.objectData]; log.info(map); return map;


3. Post-Sync Custom Function Execution

After the function executes, the mapped values modified in the function will be updated, while the enterprise ID and object apiName remain unchanged.

Test Scenario:
Data synchronization proceeds unaffected, with data passed to the custom function for processing. Verification requires checking the Report.

Function Code:
java Map map = ["details":context.details, "objectData":context.data, "afterSync": "yes001"]; return map;

Key Input Fields

(If unsure about input fields, first write the function and log the inputs. Note that inputs differ for create and update operations.)

Pre-Sync (After Data Validation):
Primary Input Fields:
json { "destObjectApiName": "", // Target object apiName "sourceData": {"fieldApiName": "fieldValue"} // Object field info }

Simple Example:
java log.info(syncArg); String destObjApiName = syncArg["destObjectApiName"] as String; log.info("destObjectApiName: " + destObjApiName); if ("object_xo21i__c" == destObjApiName) { return syncArg; } Map objectData = syncArg["objectData"] as Map; String customerCode = objectData["customerCodeHead"] as String; syncArg["isExec"] = false; // Setting this to false filters out the data from synchronization return syncArg;

During Sync (Before Writing to Target System):
Primary Input Fields:
json { "destDetailSyncDataIdAndDestDataMap": {"": {"fieldApiName": "fieldValue"}}, // Target Sub-object details "destData": {"fieldApiName": "fieldValue"} // Target object field info // For new Primary Objects, data is in `destData`; Sub-objects are in `destDetailSyncDataIdAndDestDataMap`. // For updates, both Primary and Sub-object data reside in `destData` (updates are processed separately). }

Simple Example:
java // Modify the `customerShortName` field value log.info(syncArg); syncArg["objectData"]["customerShortName"] = "dddddd"; log.info(syncArg); return syncArg;

Post-Sync (After Writing to Target System):
Primary Input Fields:
json { "sourceDataId": "5fead1146660700001170e3d", // Source data ID (only for CRM → ERP direction) "sourceObjectApiName": "AccountObj", // Source object apiName "completeDataWriteResult": { // Target data write results "detailWriteResults": [], // Sub-object results "errCode": 0, "success": true, "destEventType": 1, "errMsg": "success", "writeResult": { // Primary object result (errCode `s106240000` = success; others = failure) "errCode": 5001, "success": false, "syncDataId": "3ab1c2c2ffe04111b3e713632d5a4f76", "errMsg": "Preprocessing error: External HTTP call failed. Error: 100, SAP BP name already exists. ::errCode=s306240003", "destDetailSyncDataIdAndDestDataMap": {} } }, "destObjectApiName": "AccountObj_1el03su6s", // Target object apiName "objectData": { // Target data "tenant_id": "706089", // Enterprise ID "object_describe_api_name": "AccountObj_1el03su6s", // Object apiName "_id": "5fead2696532bf0001e524e4" }, "details": {} // Sub-object data }

Simple Example:
java // CRM → ERP: Write target object ID back to CRM source object log.info(syncArg); if (syncArg["objectData"]["_id"] != null && syncArg["objectData"]["_id"] != "" && syncArg["sourceDataId"] != null && syncArg["sourceDataId"] != "") { String destDataId = syncArg["objectData"]["_id"] as String; // Target data ID String sourceDataId = syncArg["sourceDataId"] as String; // Source data ID String errCode = syncArg["completeDataWriteResult"]["writeResult"]["errCode"] as String; log.info("--" + errCode); if (errCode == "0") { // && destDataId.length() < 11 def (Boolean error, Map data, String errorMessage) = Fx.object.update("AccountObj", sourceDataId, ["field_b25i7__c": destDataId], true); log.info(errorMessage); } } return syncArg;


4. Accessing K3 WebAPI Data

```java // Define request params: URL, username, password, data center
String url = “”; // e.g., http://jxsz.fortiddns.com:58000/k3cloud/
String userName = “”;
String passWord = “”;
String acctId = “”; // e.g., 62415a905fb572

// Request headers
Map headMap = [“Content-Type”: “application/json”];

// Kingdee K3C auth handling (requires URL, username, password, language, data center ID, method name)
String login = “Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc”;
// Query method
String queryMethod = “Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExecuteBillQuery.common.kdsvc”;
Map bodyMap1 = [:];
Map returnMap = [:];

bodyMap1.put(“lcid”, “2052”);
bodyMap1.put(“userName”, userName);
bodyMap1.put(“passWord”, passWord);
bodyMap1.put(“acctId”, acctId);
StringBody body = StringBody.builder().content(bodyMap1).build();

Request request = Request.builder()
.method(“POST”)
.url(url + login)
.timeout(7000)
.retryCount(0)
.header(“Content-Type”, “application/json”)
.body(body)
.build();

def (Boolean error1, HttpResult data1, String errorMessage1) = Fx.http.execute(request);
if (error1) {
log.info(“Login API error: “ + errorMessage1);
}

// Extract kdservice-sessionid and ASP.NET_SessionId from login response
Map headers = data1[“headers”] as Map;
if (headers == null) {
Fx.message.throwErrorMessage(“Login failed: “ + data1);
}

String Cookies = headers[“Cookies”] as String;
log.info(“Cookies: “ + Cookies);
String sessionid = Cookies.split(“;”)[0];
String NET_SessionId = Cookies.split(“;”)[1];
sessionid = sessionid.split(“=”)[1];
NET_SessionId = NET_SessionId.split(“=”)[1];

// Request body
Map bodyMap = [:];
Integer TopRowCount = 0;
String FieldKeys = “FCUSTID,FNumber”;
String FormId = “BD_Customer”;
String FilterString = “FNumber != ‘’”;
Integer Limit = 10;
Integer StartRow = 0;

// Build query params
Map data = [“TopRowCount”: TopRowCount, “FieldKeys”: FieldKeys, “FormId”: FormId, “FilterString”: FilterString, “Limit”: Limit, “StartRow”: StartRow];
bodyMap.put(“data”, data);

// Query data
StringBody body1 = StringBody.builder().content(bodyMap).build();
Request request1 = Request.builder()
.method(“POST”)
.url(url + queryMethod)
.timeout(7000)
.retryCount(0)
.header(“Content-Type”, “application/json”)
.header(“kdservice-sessionid”, sessionid)
.header(“ASP.NET_SessionId”, NET_SessionId)
.body(body1)
.build();

def (Boolean error, HttpResult Result, String Message) = Fx.http.execute(request1);
log.info(“Response data: “ + Result);

// Process data
String contentStr = Result[“content”] as String;
if (contentStr) {
// Handle query failure
if (contentStr.contains(“ResponseStatus”)) {
Fx.message.throwErrorMessage(contentStr);
}
// Process data if available
List contentList = Fx.json.parseList(contentStr);
log.info(“contentList: “ + contentList);
String[] keyList = FieldKeys.split(“,”);

contentList.each { item ->
List eachDataList = item as List;
log.info(“eachDataList: “ + eachDataList);
Map eachData = [:];
// Process each data entry
eachDataList.eachWithIndex { value, int i ->
eachData.put(keyList[i], value);
}
log.info(“eachData: “ + eachData);
}
} ```

Submit Feedback