在项目管理中,手动追踪并更新每个阶段(里程碑)的状态既繁琐又容易出错。利用Zoho Projects的自定义函数功能,您可以轻松实现依据底层任务状态自动更新阶段状态,从而提升流程效率与准确性。
场景与需求
设想一位销售代表使用Zoho Projects管理其所有客户跟进任务。为了清晰规划年度工作,他将一年划分为四个季度,并为每个季度设立了一个里程碑。
他希望达成的自动化目标是:当一个里程碑(如第一季度Q1)下包含的所有任务状态发生变更时,该里程碑的状态能随之自动同步更新。例如:
- 当所有任务均标记为“进行中”时,里程碑状态自动更新为“进行中”。
- 当所有任务均被“完成”时,里程碑状态也自动更新为“已完成”。
自动化配置流程
为实现上述目标,需要完成以下几个核心步骤:
- 创建自定义状态:为任务和里程碑列表定义所需的状态选项。
- 建立连接:创建一个安全、可复用的身份验证连接,使自定义函数能够与Zoho Projects API安全通信。
- 编写自定义函数:使用Deluge脚本编写核心逻辑,用于检查任务状态并更新里程碑。
- 定义工作流规则:设置触发器,在任务状态变更时自动执行上述函数。
详细操作步骤
步骤一:创建连接
连接(Connection)是Zoho为集成和自定义函数提供的安全认证层。为此用例创建连接时,需选择以下必要的OAuth范围:
Zohoprojects.portals.all
Zohoprojects.projects.all
Zohoprojects.tasks.all
Zohoprojects.milestones.all
创建成功后,即可在自定义函数中调用此连接以访问项目数据。
步骤二:编写自定义函数
在Zoho Projects的开发者控制台中创建自定义函数。您无需深厚的开发背景,只需按照逻辑提供代码并映射输入参数即可,系统会在触发器激活时自动运行。
以下是实现自动更新状态的核心Deluge脚本示例。该脚本的核心逻辑是:当某个任务被更新时,检查其所属里程碑下的所有任务状态,并根据情况决定是否更新里程碑状态。
输入参数映射:
在函数设置中,需要将从工作流规则传递过来的动态值(如portalId, projectId, taskId)映射到脚本内的参数变量。

图:自定义函数的参数设置界面,用于映射工作流传递的动态值。
函数脚本代码:
// 所需OAuth范围: Zohoprojects.portals.all, Zohoprojects.projects.all, Zohoprojects.tasks.all, Zohoprojects.milestones.all
endPointV3 = “https://projects.zoho.in/api/v3/portal/“;
endPoint = “https://projects.zoho.in/restapi/portal/“;
milestoneStatusIds = List();
milestoneStatusNames = List();
milestoneParameter = Map();
statusName = “审核中”; // 此为示例状态,请替换为您的目标状态名
indexValue = 0;
rangeValue = 100;
taskStatusId = “”;
state = true;
// 1. 获取任务布局,以了解所有状态ID
taskLayoutDetailsResponse = invokeurl
[ url :endPoint + portalId + “/projects/“ + projectId + “/tasklayouts”
type :GET
connection:“milestonestatus” // 使用您创建的实际连接名称
];
statusDetails = taskLayoutDetailsResponse.get(“status_details”);
for each status in statusDetails
{
if(status.get(“name”).notContains(statusName))
{
taskStatusId = taskStatusId + status.get(“id”) + “,”;
}
}
taskStatusId = taskStatusId.removeLastOccurence(“,”);
// 2. 获取当前被更新任务的信息,并找到其所属的里程碑ID
taskResponse = zoho.projects.getRecordById(portalId, projectId, “Tasks”, taskId, “milestonestatus”);
milestoneId = taskResponse.get(“tasks”).get(0).get(“milestone_id”);
// 3. 检查里程碑下是否存在非目标状态(statusName)的任务
id_list = taskStatusId.toList(“,”);
chunk_size = 4;
count = 0;
chunk = List();
all_chunks = List();
for each id in id_list
{
count = count + 1;
chunk.add(id);
if(count == chunk_size)
{
all_chunks.add(chunk);
chunk = List();
count = 0;
}
}
if(chunk.size() > 0)
{
all_chunks.add(chunk);
}
for each sub_chunk in all_chunks
{
param = sub_chunk;
taskParameter = Map();
taskParameter.put(“custom_status”, param.toString());
taskParameter.put(“index”, indexValue);
taskParameter.put(“range”, rangeValue);
taskParameter.put(“milestone_id”, milestoneId);
// 分批查询该里程碑下,状态为“非目标状态”的任务
tasks = zoho.projects.getRecords(portalId, projectId, “tasks”, taskParameter, 0, “milestonestatus”);
if(tasks.containKey(“tasks”)) // 如果找到了,说明里程碑下仍有任务未达到目标状态
{
state = false;
break;
}
}
// 4. 如果里程碑下所有任务均已达到目标状态,则更新里程碑状态
if(state)
{
// 获取里程碑的布局信息,以确定状态字段的ID映射
milestoneLayoutDetails = invokeurl
[ url :endPointV3 + portalId + “/projects/“ + projectId + “/milestones/layouts”
type :GET
connection:“milestonestatus”
];
milestoneCustomFieldDetails = milestoneLayoutDetails.get(“section_details”).get(0).get(“customfield_details”);
for each milestoneCustomFieldDetail in milestoneCustomFieldDetails
{
if(milestoneCustomFieldDetail.get(“api_name”).equalsIgnoreCase(“status”))
{
milestoneStatusIds = milestoneCustomFieldDetail.getJSON(“picklist_details”);
milestoneStatusNames = milestoneCustomFieldDetail.getJSON(“picklist_valuemap”);
}
}
// 找到目标状态在里程碑状态列表中的索引
indexOfReopen = milestoneStatusNames.indexOf(statusName);
// 获取当前里程碑的详情
milestoneResponse = zoho.projects.getRecordById(portalId, projectId, “milestones”, milestoneId, “milestonestatus”);
if(milestoneResponse.containKey(“milestones”))
{
milestoneStatusId = milestoneResponse.get(“milestones”).get(0).get(“status_det”).get(“id”);
}
else
{
return “无法更新无里程碑的状态”;
}
// 如果当前里程碑状态与目标状态不一致,则执行更新
if(milestoneStatusId != milestoneStatusIds.get(indexOfReopen))
{
milestoneParameter = Map();
milestoneParameter.put(“milestone_ids”, {“” + milestoneId + “”});
milestoneParameter.put(“update_fields”, {“CUSTOM_STATUSID”:”” + milestoneStatusIds.get(indexOfReopen) + “”});
updateMilestoneDetails = invokeurl
[ url :endPointV3 + portalId + “/projects/“ + projectId + “/milestones/“ + milestoneId + “/updatefieldvalue”
type :POST
parameters:toString(milestoneParameter)
connection:“milestonestatus”
];
info updateMilestoneDetails;
}
}
return “成功”;
步骤三:定义工作流规则
创建自定义函数后,即可在Zoho Projects中创建工作流规则来触发它。这本质上是定义一个自动化的工作流。
- 进入项目设置的工作流规则部分,创建新规则。
- 设置触发器:选择“当任务更新时”。
- 添加操作:选择“执行自定义函数”,并关联上一步创建的函数。
- 映射字段:将触发任务的对象字段(如门户ID、项目ID、任务ID)映射到自定义函数的输入参数。

图:配置工作流规则,将“任务更新”事件与自定义函数绑定。
规则生效后,每当一个任务的状态被修改,系统便会自动执行该自定义函数。函数会校验其所属里程碑下的整体任务状态,并在条件满足时自动更新里程碑状态,从而实现项目阶段状态的动态、自动化管理。这种基于API集成的自动化方式,是现代化运维/DevOps和后端流程中提升效率的关键实践。