在Zoho CRM中,系统原生的审计日志(Timeline)虽然会显示"子表已更新",但通常不会方便地列出具体哪个 record_id 被删除了供后续代码使用。
由于子表行一旦被删除并保存,该数据在数据库中就不复存在了,因此要"记录"下来,通常需要通过代码开发来实现。
这里有两种最常用的解决方案:
方法一:使用客户端脚本 (Client Script) ------ 推荐用于Web端操作
这是最直接的方法。当用户在浏览器中点击子表行的"垃圾桶"图标时,客户端脚本可以拦截该事件,获取即将被删除的行ID,并将其存入主记录的一个隐藏字段中。
适用场景: 用户在PC浏览器界面手动操作。
局限性: 手机App或API更新不触发。
步骤:
- 在主模块中创建一个长文本字段 或多行文本字段 (例如命名为
Deleted_Subform_IDs),建议在布局中隐藏。 - 进入 设置 (Setup) -> 定制 (Customization) -> 客户端脚本 (Client Script)。
- 新建脚本,选择对应的模块和布局。
- 事件类型 (Event) 选择
Delete Row(针对子表单)。 - 编写如下逻辑的代码:
javascript
/**
* logDeletedId
* @param {object} ZDKUtil - The ZDKUtil object
* @param {object} context - The context object
*/
// 获取被删除行的数据
var deletedRow = context.row;
var deletedRecordId = deletedRow.id; // 获取子表行的系统ID
// 如果是新添加但未保存的行,没有ID,不需要记录
if (!deletedRecordId) {
return;
}
// 获取主记录目前的"已删除ID存储字段"的值
var mainFieldApiName = "Deleted_Subform_IDs"; // 请替换为你创建的字段API名称
var currentLog = ZDK.Page.getField(mainFieldApiName).getValue() || "";
// 将新的删除ID追加进去(可以用逗号分隔)
var newLog = currentLog + deletedRecordId + ",";
// 更新隐藏字段的值
ZDK.Page.getField(mainFieldApiName).setValue(newLog);
后果: 当用户点击保存按钮后,主记录的 Deleted_Subform_IDs 字段里就会包含刚刚删除的ID。你可以再通过工作流(Workflow)触发后端Deluge脚本来处理这些ID(比如归档到另一个模块),处理完后清空该字段。
方法二:使用 Deluge 脚本进行"快照比对" (Snapshot Logic) ------ 适用于后端/通用
这种方法的逻辑是:在记录保存时,对比"保存前存在的子表ID列表"和"保存后存在的子表ID列表"。
核心难点: 在工作流触发时,你通常只能获取到"最新"的数据。不仅如此,Old Data 变量通常不包含子表的详细行数据。
解决方案: 你需要创建一个辅助字段(长文本),用来始终存储"上一次保存时的所有子表ID"。
步骤:
-
创建字段: 在主模块创建一个长文本字段,命名为
Snapshot_Subform_IDs(设为隐藏)。 -
编写工作流(Workflow):
- 触发器:记录创建 或 编辑 (Create or Edit) -> 成功保存后。
- 动作:执行自定义函数 (Deluge Function)。
-
Deluge 代码逻辑:
javascript
// 输入参数: recordId (主记录ID)
// 获取该记录的最新数据(包含当前的子表)
cw_record = zoho.crm.getRecordById("Deals", recordId); // 假设模块是 Deals
current_subforms = cw_record.get("Product_Details"); // 假设子表API名为 Product_Details
// 1. 构建当前的ID列表
current_id_list = List();
if(current_subforms != null)
{
for each row in current_subforms
{
// 注意:不同模块子表ID键名可能不同,通常是 id
row_id = row.get("id");
if(row_id != null)
{
current_id_list.add(row_id);
}
}
}
// 2. 获取旧的ID列表(从我们自定义的快照字段中读取)
old_snapshot_str = ifnull(cw_record.get("Snapshot_Subform_IDs"),"");
// 将字符串转回List (假设我们用逗号分隔)
old_id_list = if(old_snapshot_str.len() > 0, old_snapshot_str.toList(","), List());
// 3. 找出已删除的ID (在旧列表中存在,但不在新列表中)
deleted_ids = List();
for each old_id in old_id_list
{
if(!current_id_list.contains(old_id))
{
deleted_ids.add(old_id);
}
}
// --- 在这里处理已删除的ID ---
if(deleted_ids.size() > 0)
{
info "检测到已删除的子表ID: " + deleted_ids;
// 你可以在这里写代码:
// 1. 发送邮件通知
// 2. 创建一条"已删除记录"的日志到自定义模块
}
// 4. 关键步骤:更新快照字段,为下一次修改做准备
// 将当前列表转为逗号分隔字符串
new_snapshot_str = current_id_list.toString().replaceAll("\[|\]","").replaceAll(", ",","); // 简单的格式化
update_map = Map();
update_map.put("Snapshot_Subform_IDs", new_snapshot_str);
zoho.crm.updateRecord("Deals", recordId, update_map);
初次使用注意:
第一次运行此脚本时,因为 Snapshot_Subform_IDs 是空的,系统会判定没有删除,只是把当前的ID写入快照。从第二次保存开始,也就是先有快照后,该逻辑就能准确捕捉到删除了哪些行。
方法比较
| 特性 | 客户端脚本 (Client Script) | Deluge 快照比对 (Snapshot) |
|---|---|---|
| 实时性 | 定义在点击删除图标的那一刻 | 定义在记录保存之后 |
| 适用范围 | 仅限网页版 UI 操作 | 网页、手机App、甚至API更新都有效 |
| 实现难度 | 中等 (需懂JS) | 高 (需处理列表比对逻辑) |
| 数据准确性 | 很高 | 很高 (但需要先有一条基准数据) |
总结建议:
如果你的初衷是为了防止员工误删,或者审计员工在网页上的操作,方法一 (Client Script) 最简单且用户体验最好。如果你需要通过API同步或其他系统集成的数据清理监控,方法二 是唯一的选择。