基本概念及字段说明参考 :3.按照CRM对象对接配置说明 | 纷享销客 | 帮助中心 ,本文只说明ipaas对接方法
1、云星空辅助资料对接
对接方案:云星空的辅助资料的数据,对接到CRM上的辅助资料对象,CRM上其它对象查找关联辅助资料
第一步 在CRM新建自定义对象-辅助资料,字段设置如下,上级资料字段查找关联辅助资料
第三步 建立辅助资料同步策略,字段映射按照下表配置
设置字段映射
配置好字段映射,开启同步策略即可。
流详情
2、云星空即时库存对接
第一步 在库存管理-插件管理,设置对接插件同步机制为“同步库存明细”,CRM才会有库存明细对象
第二步创建对象同步的集成流
trigger为K3C连接器的实时库存查询轮询
trigger选择需要对接的字段
映射等待选择查找关联数据的集成流
第四步 配置字段映射,基本字段配置如下
配置映射关系节点
3、销售价目表对接
K3 Cloud中对象是销售价目表(BD_SAL_PriceList),需要对接到CRM中两个对象价目表(PriceBookObj)和可售范围(AvailableRangeObj)
第一步,检查配置项:价格管理配置-价格管理配置,已开启价目表和可售范围
第二步:创建集成流,销售价目表对接价目表。trigger选择查询单据,表单基本配置如图,选择需要对接的字段
对象映射如图
详细字段映射如图
特殊字段说明:时间转换使用公式:data2long,适用客户使用固定值
第三步:创建集成流,销售价目表对可售范围。trigger选择查询单据,表单基本配置如图,选择需要对接的字段
对象映射如图
详细字段映射如图
特殊字段说明,适用组织、可售客户范围、可售产品范围均使用固定值。客户:{"type":"FIXED","value":"FIXED"}、组织:{"type":"ALL","value":"ALL"}、产品:{"type":"FIXED","value":"FIXED"}。
4.多单位对接
需要对接单位和多单位关联表两个对象,对象对接如图
第一步:检查配置项:商品/产品管理-商品/产品管理,多单位开启开关 已打开,多单位模式为多个单位模式
第二步:创建单位对接集成流,trigger触发事件选择单据轮询,表单信息如图
字段映射
字段映射详情
创建映射关系
第三步,创建多关联表集成流,trigger触发事件选择单据轮询,表单信息如图
映射等待选择关联单位和产品集成流
配置多单位关联表字段映射
字段映射详情
保存映射关系
第四步,对接单位后,对接产品的时候,使用中间表
5.云星空单据上查
云星空通过下推生成的单据,同步对接的时候需要获取源单据,就会使用单据上查
第一步,获取源单,获取K3C单据后,新增一个action节点,执行动作选择查找源单据
起始表单为需要上查的单据,目标表单为固定的销售订单,条目名称用于在同一张 K3C 单据存在多条“来源单据追溯路由”时,明确要按哪个分录实体追溯上游单据。
输出target信息为销售订单信息,trace为中间过程表单信息(如果有)
第二步,使用上查结果。
如要将上查结果,写入到获取单据的明细中,使用一个函数 action,入参使用tuple函数,将获取单据的明细及上查到的销售订单一起作为入参,输出则为重新组装后的明细。后续就可以直接使用函数节点的输出对接
函数:
Map<String, Object> syncArgMap = (Map<String, Object>) syncArg
List<Object> functionArg = (List<Object>) syncArgMap.get("functionArg")
List<Map<String, Object>> receiveEntries =
(List<Map<String, Object>>) functionArg.get(0)
Map<String, Object> saleOrder =
(Map<String, Object>) functionArg.get(1)
Object salesOrderComId = saleOrder.get("number")
receiveEntries.each { Map<String, Object> item ->
item.put("SalesOrderComId", salesOrderComId)
}
return [
dataItems: receiveEntries
]
输出:
6.产品组合对接物料清单
CPQ对接方案说明及字段增加参考:【金蝶云星空】CPQ对接新方案(使用CRM产品组合对象)
6.1 CRM支持选配并生成标准BOM,同步到ERP
1.物料对接到CRM的时候增加套件字段对接
2.产品组合完整节点配置如图
条件通行:产品组合 BOM类型 等于 标准BOM
函数过滤选配明细:父Bomid 不为空
Map syncArgMap = (Map) (syncArg ?: [:])
Map sourceMap = syncArgMap
// 如果 BOMObj 放在 functionArg 中,则从 functionArg 读取。
if (syncArgMap['functionArg'] instanceof Map) {
sourceMap = (Map) syncArgMap['functionArg']
}
List bomList = (List) (sourceMap['BOMObj'] ?: [])
List filteredBomList = []
bomList.each { Object bomObjItem ->
Map bomItem = (Map) bomObjItem
// 保留 parent_bom_id 不为 null 的数据。
if (bomItem.containsKey('parent_bom_id') &&
bomItem['parent_bom_id'] != null) {
filteredBomList.add(bomItem)
}
}
// 返回 Map 结构。
Map result = new LinkedHashMap()
result.put('BOMObjNew', filteredBomList)
return result
保存物料清单核心字段(只展示了已配置的字段)
产品选配明细绑定函数节点过滤后输出的明细
创建3组映射关系
最后更新CRM 产品组合对象的ERP版本号字段
3.销售订单完整节点配置如图(本方案编辑订单没有采用新变更单,如果使用新变更单对接,只需要判断下中间表是否存在,不存在走创建,存在走更新变更单即可,配置相同)
数据范围:是否生成标准BOM 等于 是
函数节点入参配置
/*
* CPQ 销售订单产品预处理
*
* 入参:
* syncArg.functionArg.SalesOrderProductObj
*
* 出参:
* syncArg.functionArg.SalesOrderProductObj
*
* 输出明细会补齐原同步前函数赋值的字段:
* std_bom_core__c
* product_type__c
* son_bom_id__c
* parent_product__c
*
* 后续 K3 字段映射保持原配置不变。
*/
/**
* CPQ 销售订单产品预处理
*
* 函数入参:
* syncArg
*
* 实际数据路径:
* syncArg.functionArg.SalesOrderProductObj
*
* 函数出参:
* functionArg
*
* 输出新增:
* SalesOrderProductObjNew
*
* 处理逻辑:
* 1. 按 order_by 升序排列,null 放最后
* 2. 判断 standard / parent / son / skip
* 3. 给明细补充原字段:
* - product_type__c
* - std_bom_core__c
* - son_bom_id__c
* - parent_product__c
* 4. 过滤 skip 明细
*/
Map syncArgMap = (Map) (syncArg ?: [:])
Map functionArg = (Map) (syncArgMap['functionArg'] ?: [:])
List sourceLines = (List) (functionArg['SalesOrderProductObj'] ?: [])
List<Map> lines = []
// 转换为 Map 列表,保留原始明细字段。
sourceLines.each { Object item ->
if (item instanceof Map) {
lines.add((Map) item)
}
}
/*
* 按 CRM 原同步链路的方式排序:
* order_by 升序,order_by 为 null 的数据放最后。
*/
lines.sort { Object firstObj, Object secondObj ->
Map first = (Map) firstObj
Map second = (Map) secondObj
Object firstOrderValue = first['order_by']
Object secondOrderValue = second['order_by']
// 两个值都为空时保持原顺序。
if (firstOrderValue == null && secondOrderValue == null) {
return 0
}
// 空值放最后。
if (firstOrderValue == null) {
return 1
}
if (secondOrderValue == null) {
return -1
}
try {
BigDecimal firstOrder = new BigDecimal(String.valueOf(firstOrderValue))
BigDecimal secondOrder = new BigDecimal(String.valueOf(secondOrderValue))
return firstOrder.compareTo(secondOrder)
} catch (Exception ignored) {
// 非数字排序值按字符串升序处理。
return String.valueOf(firstOrderValue)
.compareTo(String.valueOf(secondOrderValue))
}
}
// key: 订单产品明细 ID;value: 产品分类。
Map<String, String> detailId2Type = [:]
// key: prod_pkg_key;value: 对应订单产品明细。
Map<String, Map> pkg2ObjMap = [:]
// key: 订单产品明细 ID;value: 对应订单产品明细。
Map<String, Map> id2ObjMap = [:]
// 已经确定不需要同步的节点 ID。
Set<String> sonSkipIds = [] as Set<String>
/*
* 第一遍:按排序后的顺序识别产品类型。
*
* 因为父项 order_by 在子项之前,
* 所以子项处理时可以找到父项。
*/
lines.each { Map detail ->
String id = detail['_id'] == null ?
null : String.valueOf(detail['_id'])
if (!id) {
return
}
id2ObjMap[id] = detail
Object bomId = detail['bom_id']
// 没有 BOM 的记录是普通标准产品。
if (bomId == null || String.valueOf(bomId).trim() == '') {
detailId2Type[id] = 'standard'
return
}
String pkg = detail['prod_pkg_key'] == null ?
null : String.valueOf(detail['prod_pkg_key'])
String parentPkg = detail['parent_prod_pkg_key'] == null ?
null : String.valueOf(detail['parent_prod_pkg_key'])
// 保存当前产品组合节点,供后续子项查找。
if (pkg != null && pkg.trim() != '') {
pkg2ObjMap[pkg] = detail
}
/*
* parent_prod_pkg_key 为空,表示当前节点是 BOM 根节点。
*/
if (parentPkg == null || parentPkg.trim() == '') {
boolean isPackage =
detail['is_package'] == true ||
String.valueOf(detail['is_package']) == 'true'
if (isPackage) {
// 套件根节点,作为父项同步。
detailId2Type[id] = 'parent'
} else {
// 非套件根节点,作为标准产品同步。
detailId2Type[id] = 'standard'
// 该节点的子项不再同步。
sonSkipIds.add(id)
}
return
}
/*
* 当前节点有 parent_prod_pkg_key,表示它是子节点。
*/
Map parentDetail = pkg2ObjMap[parentPkg]
String parentId = parentDetail == null ||
parentDetail['_id'] == null ?
null : String.valueOf(parentDetail['_id'])
/*
* 如果父项不存在,或者父项已被跳过,
* 当前节点也标记为 skip。
*/
if (parentId == null || sonSkipIds.contains(parentId)) {
detailId2Type[id] = 'skip'
sonSkipIds.add(id)
return
}
// 正常套件子项。
detailId2Type[id] = 'son'
/*
* 如果当前子项本身还是一个产品组合,
* 当前节点保留,但它的下级不再继续同步。
*/
Object bomCoreId = detail['bom_core_id']
if (bomCoreId != null &&
String.valueOf(bomCoreId).trim() != '') {
sonSkipIds.add(id)
}
}
/*
* 第二遍:
* 1. 补齐原同步前函数生成的字段;
* 2. 过滤 skip 明细;
* 3. 生成 SalesOrderProductObjNew。
*/
List<Map> newDetails = []
detailId2Type.each { String detailId, String type ->
Map detail = id2ObjMap[detailId]
if (detail == null) {
return
}
detail['product_type__c'] = type
/*
* 先补齐字段。
* 这样即使某些类型没有值,输出结构中也能看到字段。
*/
detail['std_bom_core__c'] = null
detail['son_bom_id__c'] = null
detail['parent_product__c'] = null
/*
* 套件父项:
*
* standard_bom_id -> std_bom_core__c
* std_bom_core__c -> FBomId.FNumber
*/
if (type == 'parent') {
detail['std_bom_core__c'] = detail['standard_bom_id']
}
/*
* 套件子项:
*
* standard_bom_line_id -> son_bom_id__c
* son_bom_id__c -> FBOMEntryId
*
* 根套件 product_id -> parent_product__c
* parent_product__c -> FParentMatId.FNumber
*/
if (type == 'son') {
String rootPkg = detail['root_prod_pkg_key'] == null ?
null : String.valueOf(detail['root_prod_pkg_key'])
Map rootDetail = rootPkg == null ?
null : pkg2ObjMap[rootPkg]
detail['son_bom_id__c'] = detail['standard_bom_line_id']
if (rootDetail != null) {
detail['parent_product__c'] = rootDetail['product_id']
}
}
/*
* skip 明细不进入后续 K3 销售订单明细。
*/
if (type == 'standard' ||
type == 'parent' ||
type == 'son') {
newDetails.add(detail)
}
}
/*
* 保留原始 SalesOrderProductObj,
* 同时新增过滤后的 SalesOrderProductObjNew。
*/
functionArg['SalesOrderProductObjNew'] = newDetails
return functionArg
增加映射等待节点,依赖产品组合和产品选配明细的中间表
保存K3C订单节点,action选择保存,提交,审核单据,配置如图
要点:Is Entry Batch Fill 要设为false
明细要绑定函数转换后的明细列表,要点,明细的产品类型字段要挪到Primary Key之后(字段管理里配置)
剩余明细字段
创建映射关系