Published at: 2025-10-30
4.2 [Function Component] Custom Function Description
Notes:
- Pre-sync, mid-sync, and post-sync functions must consistently use a Map parameter named syncArg. Return values must also be of type Map.
- Within a post-sync function, avoid using Fx.object.update together with fields that are already mapped (including fields used by data scope), to prevent circular synchronization caused by modifying data after sync. This rule is validated when enabling an Integration Platform flow. If you are certain no circular sync will be triggered but still need the above pattern, contact the Integration Platform engineering team for a bypass solution.
1. Properties of syncArg
2. Pre-sync function
Use pre-sync custom functions for data filtering
Map map = ["details":syncArg.details, "objectData":syncArg.objectData, "isExec":false, "isCover":false];
return map;<h1 class="editor_js--header">3. Mid-sync function</h1><h3 class="editor_js--header">Modify data in the mid-sync custom function, then write the modified data to the destination system</h3>log.info("Event type: " + syncArg.destEventType);
log.info(syncArg.objectData);
log.info(syncArg.details);
Map objectData = syncArg.objectData as Map;
objectData.name= "Field modified by custom function"
Map map = ["details":syncArg.details, "objectData":syncArg.objectData];
log.info(map);
return map;</code><h1 class="editor_js--header">4. Post-sync function</h1><h3 class="editor_js--header">Write back ERP order ID and Code to CRM after a K3Cloud order sync</h3><div class="editor_js--paragraph editor_js--paragraph__align-left">Use case: As described above.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Example function:</div>//Target object: order
def sourceObjectApiName = 'SalesOrderObj'
//Field to write back ERP ID
def erpIdF = "field_86Xfn__c"
//Field to write back ERP Code
def erpNoF = "field_K4xdf__c"
def destEventType = syncArg.destEventType
def completeDataWriteResult = syncArg.completeDataWriteResult as Map
if( syncArg.sourceObjectApiName!= sourceObjectApiName||
completeDataWriteResult.destEventType!=1||
!completeDataWriteResult.success
){
log.info("Ignore")
//Return directly in these cases:
//Not processing an order
//Not a create event
//Write was not successful
return syncArg;
}
log.info("Start writing back ID")
String sourceDataId = syncArg.sourceDataId
def writeResult = completeDataWriteResult.writeResult as Map
String destDataId = writeResult.destDataId
def split = destDataId.split("#", 2)
def upArg = [(erpIdF):split[0],(erpNoF):split[1]]
log.info(upArg)
def upR = Fx.object.update(sourceObjectApiName, sourceDataId, upArg )
log.info("Write-back result: "+upR)<h1 class="editor_js--header">5. Proxy (escape) custom functions — calling ERP APIs for format conversion</h1><div class="editor_js--paragraph editor_js--paragraph__align-left">If an ERP native API is not a standard open API and it is not preconfigured in the Integration Platform, call the ERP API via a proxy custom function and convert the response to the Integration Platform standard API format.</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><div class="editor_js--paragraph editor_js--paragraph__align-left">Do not place conditional filtering inside the proxy custom function. Use data scope when it suffices; otherwise use a pre-sync function.</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><div class="editor_js--paragraph editor_js--paragraph__align-left">Function parameter type must be Map and the parameter name must be syncArg. Inspect each API call’s parameters by logging: log.info(“Request params:”+syncArg);</div>queryMasterBatch receives system-passed parameters
//Call parameters:
log.info("Request params:"+syncArg);
Integer offset=syncArg["objectData"]==null?0:(Integer)syncArg["objectData"]["offset"];
Integer limit=syncArg["objectData"]==null?10:(Integer)syncArg["objectData"]["limit"];
Long startTime=syncArg["objectData"]==null?0:(Long)syncArg["objectData"]["startTime"];
Long endTime=syncArg["objectData"]==null?0:(Long)syncArg["objectData"]["endTime"];</code>queryMasterById receives system-passed parameters
//Call parameters:
log.info("Request params:"+syncArg);
String dataId=syncArg["objectData"]["dataId"];</code><h1 class="editor_js--header">6. Asynchronous inbound push via proxy custom function</h1><div class="editor_js--paragraph editor_js--paragraph__align-left">Endpoint URL: https://www.fxiaoke.com/erp/syncdata/open/objdata/asyncpush
Use case: When a client pushes data in a format that differs from platform requirements, use a custom function to transform the incoming payload into the platform-required format.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Push flow: External systems call the Integration Platform push endpoint to put data into the platform cache. The platform pulls from the cache asynchronously. If writing to the cache fails, the endpoint returns an error immediately. If processing fails after pulling from cache, inspect the data maintenance area in the Integration Platform to diagnose the error.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">HTTP method: POST</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Request Header parameters:</div><div class="editor_js--table"><table><tbody><tr><td>Field</td><td>Description</td></tr><tr><td>token</td><td>Authentication token (contact ShareCRM engineering to obtain)</td></tr><tr><td>tenantId</td><td>Contact ShareCRM engineering to obtain</td></tr><tr><td>dataCenterId</td><td>Not required for single ledger; required and must be provided for multi-ledger</td></tr><tr><td>objectApiName</td><td>ERP-side actual object name. Find via Data Sync Settings → ERP Object Settings → ERP Object Code</td></tr><tr><td>version</td><td>v1</td></tr><tr><td>operationType</td><td>3 = invalidate; for other statuses this field is not required</td></tr></tbody></table></div><div class="editor_js--paragraph editor_js--paragraph__align-left">Push steps:</div><div class="editor_js--paragraph editor_js--paragraph__align-left">1. Create ERP object</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><div class="editor_js--paragraph editor_js--paragraph__align-left">2. Create a sync policy.</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><div class="editor_js--paragraph editor_js--paragraph__align-left">3. Add the push custom function (required for both standard and non-standard pushes using the push endpoint).</div>log.info("Request params:"+Fx.json.toJson(syncArg))
String dataStr=syncArg["objectData"]["pushData"];
Map pushDataMap=Fx.json.parse(dataStr);
List<Map> pushDatas=[]; if( pushDataMap["data"] instanceof List){// /* Example of pushing a single item: { "data": { "number": "A2001", "lrap_days": "2", "name": "Test Product 41" } } / pushDatas=pushDataMap["data"] as List }else if( pushDataMap["data"] instanceof Map ){ / Example of pushing multiple items: { "data": [ { "number": "A2001", "lrap_days": "2", "name": "Test Product 41" } ] } */ pushDatas.add(pushDataMap["data"] as Map) }
List resultList=[]
pushDatas.each{
map->
Map dataMap=map as Map;
String id=map["code"];//Choose a unique identifier field in the Primary Object
dataMap.put("id",id);//Must include this key and value
Map data=["masterFieldVal":dataMap]//Convert to platform format
resultList.add(data)
}
return ["dataList":resultList];//Return an array</code><div class="editor_js--paragraph editor_js--paragraph__align-left">Example: push failure</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><div class="editor_js--paragraph editor_js--paragraph__align-left">Push success</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><h1 class="editor_js--header">7. Scenario functions</h1><h3 class="editor_js--header">Trigger synchronization from source system to destination system by source data ID (requires getById implementation)</h3><div class="editor_js--paragraph editor_js--paragraph__align-left">Use case: Trigger synchronization for a specific source data ID.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Example function:</div>Map header=[:]
Map params1=[:]
params1.put("erpObjectApiName","xxObj"); //Destination object API name (parameter name historically CRM->ERP oriented)
params1.put("crmDataId","id"); //Source data ID (historical naming CRM->ERP oriented)
params1.put("crmObjectApiName","xxxobj"); //Source object API name
Map commonParam=["type":"manualSyncCrm2Erp","params":Fx.json.toJson(params1)];
def result1=Fx.proxy.callAPI("erp.syncData.executeCustomFunction",header,commonParam);
log.info(result1)<h3 class="editor_js--header">Trigger ERP->CRM sync/update using CRM data ID (requires an existing mapping)</h3>// Required: CRM object API name
def crmObjApiName = context.data.object_describe_api_name;
// Required: CRM data ID
def crmDataId = context.data._id
// Required: ERP intermediary object API name
def erpObjApiName = "BD_Customer.BillHead"
// Type used to trigger ERP->CRM sync using CRM data ID
def type = "manualSyncErpDataByCrmDataId"
def params = ["crmObjectApiName":crmObjApiName,
"crmDataId":crmDataId,
"erpObjectApiName":erpObjApiName]
def arg = ["type":type,
"params":Fx.json.toJson(params)]
def ret = Fx.proxy.callAPI("erp.syncData.executeCustomFunction", [:], arg)
// Handle sync result:
log.info(ret)<h3 class="editor_js--header">Calling Kingdee K3Cloud WebApi</h3><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div>// The code below demonstrates how to call webapi from ShareCRM.
// K3Cloud base URL String baseUrl = "http://172.31.100.60/K3Cloud/" // API endpoint identifiers String auth = "Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc" String query = "Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExecuteBillQuery.common.kdsvc" String save = "Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Save.common.kdsvc" String submit = "Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Submit.common.kdsvc" String audit = "Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Audit.common.kdsvc" // Login credentials List param = ["5ec229fad54306", "ces", "888888", 2052] // Request body Map body = ["parameters": param] Map headers = ["Content-Type": "application/json; charset=utf-8"] Map content = [:] // Login request def (Boolean error, HttpResult data, String errorMessage) = Fx.http.post(baseUrl + auth, headers, body) Map cookieHeader = [:] if (!error) { if (data.statusCode == 200) { content = json.parse(data.content as String) String logInType = content."LoginResultType" if (logInType == "1" || logInType == "-5") { log.info("Login successful, content:" + content)
} else {
log.info("Login failed, content:" + content)
}
} else {
log.info("Request failed, HttpResult:" + data)
}
} else { log.info("Request error, errorMessage:" + errorMessage)
} // Example: inventory query body.put("parameters", [ [ "FormId" : "STK_Inventory", "FieldKeys" : "FID,FStockId.FNumber,FMaterialId.FNumber,FMaterialId.FName,FLot.FNumber,FLot,FStockLocId,FBaseQty,FBaseAVBQty,FUpdateTime", "FilterString": "FMaterialId.FNumber='CH4453'", "OrderString" : "", "TopRowCount" : "0", "StartRow" : "0", "Limit" : "0" ] ]) (error, data, errorMessage) = Fx.http.post(baseUrl + query, headers, body) log.info(data)
/—————————Create order example—————————————/
// Get order data from CRM
Map salesOrderMap = context.data log.info("Order:"+json.toJson(salesOrderMap))
// Get product data (one)
// Order product
Map salesProductMap = ((List)context.details.SalesOrderProductObj)[0]
log.info("Order product:"+salesProductMap)
// Product Map productData
(error, productData, errorMessage) = object.findById("ProductObj", salesProductMap.product_id as String) log.info("product:"+productData)
// Create order model
Map model = [:]
// Required: bill type
model.put("FBillTypeID", ["FNumber": "XSDD01_SYS"])
// Required: sales organization
model.put("FSaleOrgId", ["FNumber": "003"])
// Required: customer
model.put("FCustId", ["FNumber": "4326rtyu"])
// Required: date
model.put("FDate", "2020-05-22 00:00:00")
// Required: salesperson
model.put("FSalerId", ["FNumber": "88888"])
model.put("FSaleDeptId", ["FNumber": ""])
// K3Cloud bill number sync from CRM order
model.put("FBillNo ", salesOrderMap.name) // Order lines
Map material = [:]
// Required: material code (must be assigned to the organization)
material.put("FMaterialId", ["FNumber": productData.product_code])
// Required: sales unit
material.put("FUnitID", ["FNumber": "Pcs"])
// Quantity
material.put("FQty", salesProductMap.quantity)
// Tax-inclusive unit price
material.put("FTaxPrice", salesProductMap.sales_price)
// Required: order lines
model.put("FSaleOrderEntry", [material])
// Finance info
Map finance = [:]
// Required: settlement currency
finance.put("FSettleCurrId", ["FNumber": "PRE001"])
finance.put("FExchangeTypeId", ["FNumber": "HLTX01_SYS"])
finance.put("FExchangeRate", 1)
model.put("FSaleOrderFinance",finance)
body.put("parameters",["SAL_SaleOrder", json.toJson(["Model":model])])
log.info(json.toJson(model))
(error, data, errorMessage) = Fx.http.post(baseUrl + save, headers, body)
log.info(data)
// Submit
body.put("parameters",["SAL_SaleOrder", ["Numbers": [salesOrderMap.name]]])
(error, data, errorMessage) = Fx.http.post(baseUrl + submit, headers, body) log.info(data)
// Audit
(error, data, errorMessage) = Fx.http.post(baseUrl + audit, headers, body)
log.info(data)
// This is an example. Return values must match the custom function’s required return type.
return "111"</code><h3 class="editor_js--header">Operate the intermediary mapping table from CRM custom functions</h3>Map header=[:]
// Create sync mapping
Map param1=["ployDetailId":"155bd981457343f291e0edc13776217f",// policy detail id (see image). If policy is deleted and recreated, update this id
"sourceObjectApiName":"AccountObj",// source object API name; update if changed
"destObjectApiName":"BD_Customer.BillHead",// destination object API name; update if changed
"sourceDataId":"sourceDataId123",// source data id
"destDataId":"destDataId123666",// destination data id
"sourceDataName":"sourceDataName3666",// source data name
"destDataName":"destDataName66",// destination data name
"remark":"remark1341"];// remark
def result1=Fx.proxy.callAPI("erp.syncData.createSyncDataMapping",header,param1);
//[false, HttpResult(statusCode=200, content={"errCode":"s106240000","errMsg":"success"}, bytes=null), ] s106240000 = success; others indicate failure
log.info(result1)
// Update destination data id by source data id Map param2=["sourceObjectApiName":"AccountObj", "destObjectApiName":"BD_Customer.BillHead", "sourceDataId":"sourceDataId123", "destDataId":"destDataId123666"] def result2=Fx.proxy.callAPI("erp.syncData.updateSyncDataMapping",header,param2); log.info(result2)
// Query whether a mapping exists for source data ids
Map param3=["sourceObjectApiName":"AccountObj",
"destObjectApiName":"BD_Customer.BillHead",
"sourceDataId":["sourceDataId123"]]// source data ids list
def result3=Fx.proxy.callAPI("erp.syncData.getSyncDataMappingBySourceDataId",header,param3);
// [false, HttpResult(statusCode=200, content={"data":{"sourceDataId123":{…}},"errCode":"s106240000","errMsg":"success"}, bytes=null), ]
// s106240000 = success; others = failure
// data is a Map keyed by sourceDataId; values are the corresponding existing mappings
log.info(result3)</code><div class="editor_js--paragraph editor_js--paragraph__align-left">Locate policy detail ID ployDetailId</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><div class="editor_js--paragraph editor_js--paragraph__align-left">OA pending message sync: create mapping via CRM personnel</div>// channel valid values: ERP_K3CLOUD, ERP_SAP, ERP_U8, OA, STANDARD_CHANNEL
// "dataType":"employee" indicates creating an employee mapping
// dataCenterId is the data center id
// fsDataId is the CRM data id. For personnel, use the employee ID (not CRM person object id).
// erpDataId is the ERP data id
// fsDataName is the CRM data name
// erpDataName is the ERP data name
Map data = [ "dataCenterId":"701916***319168", "channel":"OA", "dataType":"employee_oa", "fsDataId":fsDataId, "fsDataName":fsDataName, "erpDataId":erpDataId, "erpDataName":erpDataName ];
def ret = Fx.proxy.callAPI("erp.syncData.createErpfieldmapping", [:], data);
Fx.log.info("ret is : "+ret)</code><h1 class="editor_js--header">8. Standard API interface specification</h1><div class="editor_js--paragraph editor_js--paragraph__align-left">Standard API interfaces strictly require the input parameter named syncArg (Map type) and require a Map return value. The Map’s fields must follow the format shown in Integration Platform → Connector Management → Connected Object → Generate API page — the HTTP response format must be strictly followed.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">See the generated API page for the exact JSON response structure. Always follow that format exactly.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Example of a Standard API return format:</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><div class="editor_js--paragraph editor_js--paragraph__align-left">For the K3Cloud channel, response code=0 indicates success; non-zero indicates failure. When code=0, the data field must contain values and must correctly return masterDataId for Primary Object and ID fields for Sub-objects. If the object has Sub-objects, return Sub-object IDs in the order and format required by the Integration Platform.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">For Standard and SAP channels, you may customize the field names for code, message, data and the success code. Configure them in Integration Platform → Connector Management → Connector Configuration, as shown below:</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><div class="editor_js--paragraph editor_js--paragraph__align-left">Once those fields and the success code are configured, custom functions must return responses that strictly match that configuration.</div><div class="editor_js--attaches"><div class="editor_js--attaches__file-icon" data-extension="docx" style="color: #3e74da;"></div><div class="editor_js--attaches__file-info"><div class="editor_js--attaches__title">DSS platform custom function adaptation.docx</div><div class="editor_js--attaches__size">244.6 KB</div></div></div><h2 class="editor_js--header">8.1 asyncpush — push data API notes</h2><div class="editor_js--paragraph editor_js--paragraph__align-left">asyncpush is asynchronous: a successful API response only means the request reached the Integration Platform. Actual synchronization runs asynchronously in batches.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Unless you have strict timeliness requirements, prefer the query API queryMasterBatch to retrieve data.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Benefits of using the query API: better fault tolerance, and mature tools for backfilling or re-syncing historical data without requiring customer IT involvement.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Reference for push: http://help.fxiaoke.com/2615/9bfa/b0a6/70d7 (API development)</div><h2 class="editor_js--header">8.2 create</h2><div class="editor_js--paragraph editor_js--paragraph__align-left">Called when CRM->ERP creates data.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">This API must return the ERP primary key in its response; ERP primary key cannot be returned asynchronously.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">If the ERP cannot return within 30s, the ERP side should implement idempotency. When CRM retries with the same data, return the last successful creation result.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Request example:</div>{ "objAPIName":"ERP object APIName", "masterFieldVal":"Primary data", "detailFieldVals":"Sub-object list" }<div class="editor_js--paragraph editor_js--paragraph__align-left">Response example:</div>{ "code": "error code", "message": "error message", "data": { "masterDataId": "primary key of master data", "detailDataIds": { "primary key lists for each sub-object" } } }<div class="editor_js--paragraph editor_js--paragraph__align-left">Example response screenshot:</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><h2 class="editor_js--header">8.3 update</h2><div class="editor_js--paragraph editor_js--paragraph__align-left">Called when CRM->ERP updates Primary Object. Primary and Sub-object data are fully overwritten on the destination.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Request example:</div>{ "objAPIName":"ERP object APIName", "masterFieldVal":"Primary data", "detailFieldVals":"Sub-object list" }<div class="editor_js--paragraph editor_js--paragraph__align-left">Response example:</div>{ "code": "error code", "message": "error message", "data": { "masterDataId": "primary key of master data", "detailDataIds": { "primary key lists for each sub-object" } } }<div class="editor_js--paragraph editor_js--paragraph__align-left">Example response screenshot:</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><h2 class="editor_js--header">8.4 queryMasterBatch</h2><div class="editor_js--paragraph editor_js--paragraph__align-left">ERP->CRM incremental query API. The Integration Platform polls this API periodically to retrieve incremental changes.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Request parameters:</div><div class="editor_js--paragraph editor_js--paragraph__align-left">objAPIName: ERP object APIName</div><div class="editor_js--paragraph editor_js--paragraph__align-left">startTime: data change start time (unix timestamp in milliseconds)</div><div class="editor_js--paragraph editor_js--paragraph__align-left">endTime: data change end time (unix timestamp in milliseconds)</div><div class="editor_js--paragraph editor_js--paragraph__align-left">includeDetail: whether to include Sub-object data in the response</div><div class="editor_js--paragraph editor_js--paragraph__align-left">offset: record offset</div><div class="editor_js--paragraph editor_js--paragraph__align-left">limit: number of records requested</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Response format:</div>{ "code": "error code", "message": "error message", "data": { "totalNum": "total records", "dataList": [{ "objAPIName": "Primary object name", "masterFieldVal": {primary object data}, "detailFieldVals": { "SubObject1": [{SubObject1 data list}], "SubObject2": [{SubObject2 data list}] } }, { "objAPIName": "Primary object name", "masterFieldVal": {primary object data}, "detailFieldVals": { "SubObject1": [{SubObject1 data list}], "SubObject2": [{SubObject2 data list}] } }] } }<div class="editor_js--paragraph editor_js--paragraph__align-left">Polling termination rules for a time slice:</div><div class="editor_js--paragraph editor_js--paragraph__align-left">(1) Returned record count is 0. Example: For a 6-minute slice paged over 10 pages, if page 2 returns 0 records, polling will skip pages 3+ of that slice and continue with the next time slice.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">(2) ERP API returns an error: skip this time slice and continue with the next slice.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Example response screenshot:</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><h2 class="editor_js--header">8.5 queryMasterById</h2><div class="editor_js--paragraph editor_js--paragraph__align-left">ERP->CRM fetch by primary key ID. Used when re-syncing to retrieve the latest data.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Request parameters:</div><div class="editor_js--paragraph editor_js--paragraph__align-left">objAPIName: ERP object APIName</div><div class="editor_js--paragraph editor_js--paragraph__align-left">dataId: data primary key</div><div class="editor_js--paragraph editor_js--paragraph__align-left">includeDetail: whether to include Sub-object data</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Response format:</div>{ "code": "error code", "message": "error message", "data": { "objAPIName": "ERP object APIName", "masterFieldVal": {primary object data}, "detailFieldVals": { "SubObjectAPIName": [{sub-object data list}] } } }<div class="editor_js--paragraph editor_js--paragraph__align-left">Example response screenshot:</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><h2 class="editor_js--header">8.6 invalid</h2><div class="editor_js--paragraph editor_js--paragraph__align-left">Called when CRM->ERP invalidates (voids) Primary Object data.</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Request parameters:</div><div class="editor_js--paragraph editor_js--paragraph__align-left">Response format:</div>{
"errCode": "error code",
"errMsg": "error message"
}
{
"errCode": "error code",
"errMsg": "error message"
}<div class="editor_js--paragraph editor_js--paragraph__align-left">Example response screenshot:</div><div class="editor_js--qiniu_image"><div class="editor_js--qiniu_image__picture">picture coming soon:</div><div class="editor_js--qiniu_image__caption"></div></div><h2 class="editor_js--header">8.7 Example: XML-format list query function</h2>/* List query example */
// Call parameters: log.info("Request params:"+syncArg); Integer offset=syncArg["objectData"]==null?0:(Integer)syncArg["objectData"]["offset"];// offset Integer limit=syncArg["objectData"]==null?50:(Integer)syncArg["objectData"]["limit"];// page size Long startTime=syncArg["objectData"]==null?0:(Long)syncArg["objectData"]["startTime"];// start timestamp Long endTime=syncArg["objectData"]==null?0:(Long)syncArg["objectData"]["endTime"];// end timestamp
// Optionally override request parameters here for testing/debugging
// Convert to page number Integer page=((offset+limit)/limit).toInteger(); Integer pageSize=limit;
// Convert timestamps to date/time DateTime startDt = DateTime.of(startTime); DateTime endDt = DateTime.of(endTime); Integer startmonth=startDt.month; Integer startday=startDt.day; Integer starthour=startDt.hour; Integer startminute=startDt.minute;
Integer endmonth=endDt.month; Integer endday=endDt.day;
String startDateStr=startDt.year+"-"+(startmonth<10?"0"+startmonth:startmonth)+"-"+(startday<10?"0"+startday:startday); String endDateStr=endDt.year+"-"+(endmonth<10?"0"+endmonth:endmonth)+"-"+(endday<10?"0"+endday:endday); String startTimeStr=""+(starthour<10?"0"+starthour:starthour)+":"+(startminute<10?"0"+startminute:startminute)+":00";
// Request URL: controller returns the specific endpoint by urlName Map urlParams= ["urlName":""];
// Base data request URL: func_Yd9n2__c is a controller function that encapsulates common logic/config def (error,result,errorMessage) = Fx.function.executeFunc("func_Yd9n2__c",urlParams) String requestUrl =result["url"];
// Request headers: put authentication info into controller if needed Map header=["Authorization":"Basic " +Fx.crypto.base64.encode(Fx.utils.toUTF8Bytes("username:password")) ,"Content-Type":"text/xml; charset=utf-8"];
// Request XML String requestXml = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"urn:sap-com:document:sap:soap:functions:mc-style\">\n" + " <soapenv:Header/>\n" + " <soapenv:Body>\n" + " <urn:Zcrmfu0003>\n" + " <!–Optional:–>\n" + " <Enddate>"+endDateStr+"</Enddate>\n" + " <!–Optional:–>\n" + " <Endtime>23:59:59</Endtime>\n" + " <!–Optional:–>\n" + " <Pagenum>"+page+"</Pagenum>\n" + " <!–Optional:–>\n" + " <Pagesize>"+pageSize+"</Pagesize>\n" + " <!–Optional:–>\n" + " <Rtdetail>\n" + " </Rtdetail>\n" + " <!–Optional:–>\n" + " <Rtmaster>\n" + " </Rtmaster>\n" + " <!–Optional:–>\n" + " <Startdate>"+startDateStr+"</Startdate>\n" + " <!–Optional:–>\n" + " <Starttime>"+startTimeStr+"</Starttime>\n" + " <!–Optional:–>\n" + " <Vbeln></Vbeln>\n" + " </urn:Zcrmfu0003>\n" + " </soapenv:Body>\n" + "</soapenv:Envelope>"; log.info("Request URL:"+requestUrl); log.info("Request XML:"+requestXml); def (Boolean error2,HttpResult result2,String errorMessage2)= Fx.http.post(requestUrl,header,requestXml,30000,false,0) result2["bytes"]=null;
if(result2==null){
Fx.message.throwErrorMessage("API timeout. Please edit the fields to sync and retry.");
}
if( error2 ){
log.info("Response:"+result2);
Fx.message.throwErrorMessage(errorMessage2);
}else if(result2["statusCode"]!=200){
log.info("Response:"+result2);
Fx.message.throwErrorMessage("Please check your parameters.");
}
// Parse response def xmlSlurper =new XmlSlurper(); String content=result2["content"]; def responseMap=xmlSlurper.parseText(content) def masterArrays=responseMap.Body["Zcrmfu0003Response"]["Rtmaster"] as NodeChildren; def detailArrays=responseMap.Body["Zcrmfu0003Response"]["Rtdetail"] as NodeChildren;
List<Map> dataList=[]; // You can specify return fields (if ERP field requirements change later, update here), or convert the entire payload to a Map List returnMasterFields=["Vbeln","Lfart","Vstel","Kunnr","Vkorg",……..] // masterArrays.children().each{ String masterId=it["Vbeln"]; if((masterId!= null && ""!=masterId)){ Map dataMap=[:] returnMasterFields.each{ field-> // Nodes must be converted to base types for correct serialization String fieldV=(it[field] as NodeChildren).text() as String; if(fieldV!=null&&""!=fieldV ){ dataMap.put(field,fieldV) } } dataMap["masterId"]= masterId; dataMap["Vstel"]="Factory#"+dataMap["Vstel"];// special field handling (e.g., concatenation) dataList.add(["masterFieldVal":dataMap]) } }
// If there are details, remember to process them: ["masterFieldVal":dataMap, "detailFieldVals":[["DetailAPIName1":[]]],["DetailAPIName2":[]]]]
Map resultMap=[:]; resultMap.put("dataList", dataList); return resultMap;</code><div class="editor_js--paragraph editor_js--paragraph__align-left"></div>