CodeX Scripts for Time Logs | Online Help | Zoho Projects

CodeX Scripts for Time Logs

CodeX Scripts in Zoho Projects are advanced conditional logic scripts that help enforce specific rules and validations. Users can control how and when time logs can be created, updated, blocked or transitioned.

Use Cases

Restrict Time Logs Based on Task Work Hours

Restrict users from logging time that exceeds the total work hours allocated to a task. The script also blocks logging for tasks with no work hours assigned.
Code
  1. function main() {
  2.     try {
  3.         let url = "https://projects.zoho.com";
  4.         let records = current.record;
  5.         let violations = [];

  6.         function parseToMinutes(s) {
  7.             if (!s) return 0;
  8.             let str = String(s);
  9.             if (str.indexOf(":") === -1) return (parseFloat(str) || 0) * 60;
  10.             let p = str.split(":");
  11.             return (parseInt(p[0], 10) || 0) * 60 + (parseInt(p[1], 10) || 0);
  12.         }

  13.         function formatMinutes(m) {
  14.             let h = Math.floor(m / 60);
  15.             let mins = m % 60;
  16.             return h + ":" + (mins < 10 ? "0" : "") + mins;
  17.         }

  18.         let taskLogs = [];
  19.         let projectTaskMap = {};

  20.         for (let i = 0; i < records.length; i++) {
  21.             let record = records[i];
  22.             let linked = record.module_detail;
  23.             if (!linked || linked.name !== "Task") continue;

  24.             taskLogs.push(record);
  25.             let projId = String(record.project_id);
  26.             if (!projectTaskMap[projId]) projectTaskMap[projId] = {};
  27.             projectTaskMap[projId][linked.id] = true;
  28.         }

  29.         if (taskLogs.length === 0) return;

  30.         let allTasks = [];
  31.         let projectIds = Object.keys(projectTaskMap);

  32.         if (projectIds.length === 1) {
  33.             let projId = projectIds[0];
  34.             let taskIds = Object.keys(projectTaskMap[projId]);
  35.             let filter = JSON.stringify({ criteria: [{ field_name: "id", criteria_condition: "contains", value: taskIds }], pattern: "1" });
  36.             let req = new HttpRequest();
  37.             req.url(`${url}/api/v3/portal/${current.org.zohoOrgId}/projects/${projId}/tasks?page=1&per_page=200&filter=${encodeURIComponent(filter)}`);
  38.             req.method("GET");
  39.             req.connection("zprojects");
  40.             allTasks = req.execute().asJson().statusMessage.responseText.tasks || [];
  41.         } else {
  42.             let uniqueMap = {};
  43.             for (let i = 0; i < projectIds.length; i++) {
  44.                 let ids = Object.keys(projectTaskMap[projectIds[i]]);
  45.                 for (let j = 0; j < ids.length; j++) uniqueMap[ids[j]] = true;
  46.             }
  47.             let filter = JSON.stringify({ criteria: [{ field_name: "id", criteria_condition: "contains", value: Object.keys(uniqueMap) }], pattern: "1" });
  48.             let req = new HttpRequest();
  49.             req.url(`${url}/api/v3/portal/${current.org.zohoOrgId}/tasks?page=1&per_page=200&filter=${encodeURIComponent(filter)}`);
  50.             req.method("GET");
  51.             req.connection("zprojects");
  52.             allTasks = req.execute().asJson().statusMessage.responseText.tasks || [];

  53.             let returnedIds = {};
  54.             for (let i = 0; i < allTasks.length; i++) returnedIds[String(allTasks[i].id)] = true;

  55.             let missingByProject = {};
  56.             for (let i = 0; i < projectIds.length; i++) {
  57.                 let projId = projectIds[i];
  58.                 let taskIds = Object.keys(projectTaskMap[projId]);
  59.                 for (let j = 0; j < taskIds.length; j++) {
  60.                     if (!returnedIds[taskIds[j]]) {
  61.                         if (!missingByProject[projId]) missingByProject[projId] = [];
  62.                         missingByProject[projId].push(taskIds[j]);
  63.                     }
  64.                 }
  65.             }

  66.             let missingProjIds = Object.keys(missingByProject);
  67.             for (let i = 0; i < missingProjIds.length && i < 2; i++) {
  68.                 let projId = missingProjIds[i];
  69.                 let mFilter = JSON.stringify({ criteria: [{ field_name: "id", criteria_condition: "contains", value: missingByProject[projId] }], pattern: "1" });
  70.                 let mReq = new HttpRequest();
  71.                 mReq.url(`${url}/api/v3/portal/${current.org.zohoOrgId}/projects/${projId}/tasks?page=1&per_page=200&filter=${encodeURIComponent(mFilter)}`);
  72.                 mReq.method("GET");
  73.                 mReq.connection("zprojects");
  74.                 let moreTasks = mReq.execute().asJson().statusMessage.responseText.tasks || [];
  75.                 for (let k = 0; k < moreTasks.length; k++) allTasks.push(moreTasks[k]);
  76.             }
  77.         }

  78.         let taskWorkMap = {};
  79.         let taskNameMap = {};
  80.         for (let i = 0; i < allTasks.length; i++) {
  81.             let task = allTasks[i];
  82.             let id = String(task.id);
  83.             taskNameMap[id] = task.name;
  84.             taskWorkMap[id] = {
  85.                 totalWork: parseToMinutes(task.owners_and_work.total_work),
  86.                 alreadyLogged: parseToMinutes(task.log_hours.total_hours)
  87.             };
  88.         }

  89.         let batchPerTask = {};
  90.         for (let i = 0; i < taskLogs.length; i++) {
  91.             let record = taskLogs[i];
  92.             let taskId = String(record.module_detail.id);
  93.             if (!batchPerTask[taskId]) batchPerTask[taskId] = { totalMinutes: 0, logIds: [] };
  94.             batchPerTask[taskId].totalMinutes += parseToMinutes(record.log_hour);
  95.             batchPerTask[taskId].logIds.push(record.id);
  96.         }

  97.         let batchTaskIds = Object.keys(batchPerTask);
  98.         for (let i = 0; i < batchTaskIds.length; i++) {
  99.             let taskId = batchTaskIds[i];
  100.             let info = taskWorkMap[taskId];
  101.             let batch = batchPerTask[taskId];
  102.             if (!info) continue;

  103.             if (info.totalWork === 0) {
  104.                 violations.push({ taskId: taskId, taskName: taskNameMap[taskId], totalWork: "00:00", reason: "No work hours allocated" });
  105.                 continue;
  106.             }

  107.             let totalAfterLog = info.alreadyLogged + batch.totalMinutes;
  108.             if (totalAfterLog > info.totalWork) {
  109.                 violations.push({ taskId: taskId, taskName: taskNameMap[taskId], totalWork: formatMinutes(info.totalWork), alreadyLogged: formatMinutes(info.alreadyLogged), newBatch: formatMinutes(batch.totalMinutes), reason: "Would exceed work hours" });
  110.             }
  111.         }

  112.         if (violations.length > 0) {
  113.             throw new ScriptError(JSON.stringify({ violations: violations }));
  114.         }
  115.     } catch (err) {
  116.         throw new ScriptError(err);
  117.     }
  118. }

 Restrict Time Logs for Unassigned Tasks or Issues

Allow time logging only for tasks or issues that have at least one owner assigned. Logs against unassigned work items are rejected.
Code
  1. function main() {
  2.     try {
  3.         let url = "https://projects.zoho.com";
  4.         let records = current.record;
  5.         let violations = [];

  6.         let taskLogs = [];
  7.         let projectTaskMap = {};

  8.         for (let i = 0; i < records.length; i++) {
  9.             let record = records[i];
  10.             let linked = record.module_detail;
  11.             if (!linked || linked.name !== "Task") continue;

  12.             taskLogs.push(record);
  13.             let projId = String(record.project_id);
  14.             if (!projectTaskMap[projId]) projectTaskMap[projId] = {};
  15.             projectTaskMap[projId][linked.id] = true;
  16.         }

  17.         if (taskLogs.length === 0) return;

  18.         let allTasks = [];
  19.         let projectIds = Object.keys(projectTaskMap);

  20.         if (projectIds.length === 1) {
  21.             let projId = projectIds[0];
  22.             let taskIds = Object.keys(projectTaskMap[projId]);
  23.             let filter = JSON.stringify({ criteria: [{ field_name: "id", criteria_condition: "contains", value: taskIds }], pattern: "1" });
  24.             let req = new HttpRequest();
  25.             req.url(`${url}/api/v3/portal/${current.org.zohoOrgId}/projects/${projId}/tasks?page=1&per_page=200&filter=${encodeURIComponent(filter)}`);
  26.             req.method("GET");
  27.             req.connection("zprojects");
  28.             allTasks = req.execute().asJson().statusMessage.responseText.tasks || [];
  29.         } else {
  30.             let uniqueMap = {};
  31.             for (let i = 0; i < projectIds.length; i++) {
  32.                 let ids = Object.keys(projectTaskMap[projectIds[i]]);
  33.                 for (let j = 0; j < ids.length; j++) uniqueMap[ids[j]] = true;
  34.             }
  35.             let filter = JSON.stringify({ criteria: [{ field_name: "id", criteria_condition: "contains", value: Object.keys(uniqueMap) }], pattern: "1" });
  36.             let req = new HttpRequest();
  37.             req.url(`${url}/api/v3/portal/${current.org.zohoOrgId}/tasks?page=1&per_page=200&filter=${encodeURIComponent(filter)}`);
  38.             req.method("GET");
  39.             req.connection("zprojects");
  40.             allTasks = req.execute().asJson().statusMessage.responseText.tasks || [];

  41.             let returnedIds = {};
  42.             for (let i = 0; i < allTasks.length; i++) returnedIds[String(allTasks[i].id)] = true;

  43.             let missingByProject = {};
  44.             for (let i = 0; i < projectIds.length; i++) {
  45.                 let projId = projectIds[i];
  46.                 let taskIds = Object.keys(projectTaskMap[projId]);
  47.                 for (let j = 0; j < taskIds.length; j++) {
  48.                     if (!returnedIds[taskIds[j]]) {
  49.                         if (!missingByProject[projId]) missingByProject[projId] = [];
  50.                         missingByProject[projId].push(taskIds[j]);
  51.                     }
  52.                 }
  53.             }

  54.             let missingProjIds = Object.keys(missingByProject);
  55.             for (let i = 0; i < missingProjIds.length && i < 2; i++) {
  56.                 let projId = missingProjIds[i];
  57.                 let mFilter = JSON.stringify({ criteria: [{ field_name: "id", criteria_condition: "contains", value: missingByProject[projId] }], pattern: "1" });
  58.                 let mReq = new HttpRequest();
  59.                 mReq.url(`${url}/api/v3/portal/${current.org.zohoOrgId}/projects/${projId}/tasks?page=1&per_page=200&filter=${encodeURIComponent(mFilter)}`);
  60.                 mReq.method("GET");
  61.                 mReq.connection("zprojects");
  62.                 let moreTasks = mReq.execute().asJson().statusMessage.responseText.tasks || [];
  63.                 for (let k = 0; k < moreTasks.length; k++) allTasks.push(moreTasks[k]);
  64.             }
  65.         }

  66.         let taskOwnerMap = {};
  67.         for (let i = 0; i < allTasks.length; i++) {
  68.             let task = allTasks[i];
  69.             let owners = task.owners_and_work.owners || [];
  70.             let hasReal = false;
  71.             for (let j = 0; j < owners.length; j++) {
  72.                 if (owners[j].zuid && owners[j].zuid !== 0) { hasReal = true; break; }
  73.             }
  74.             taskOwnerMap[String(task.id)] = hasReal;
  75.         }

  76.         for (let i = 0; i < taskLogs.length; i++) {
  77.             let record = taskLogs[i];
  78.             let taskId = String(record.module_detail.id);
  79.             if (taskOwnerMap[taskId] === undefined) continue;

  80.             if (!taskOwnerMap[taskId]) {
  81.                 violations.push({ logId: record.id, taskId: taskId, user: record.user ? record.user.name : "Unknown", reason: "Task is unassigned" });
  82.             }
  83.         }

  84.         if (violations.length > 0) {
  85.             throw new ScriptError(JSON.stringify({ violations: violations }));
  86.         }
  87.     } catch (err) {
  88.         throw new ScriptError(err);
  89.     }
  90. }

 Restrict Weekend Time Logs

Restrict users from logging time on Saturdays and Sundays by validating the selected log date.
Code
  1. function main() {
  2.     try {
  3.         let records = current.record;
  4.         let violations = [];

  5.         for (let i = 0; i < records.length; i++) {
  6.             let record = records[i];
  7.             let logDate = record.log_date;
  8.             if (!logDate) continue;

  9.             let parts = logDate.split("-");
  10.             let date = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]));
  11.             let day = date.getDay();

  12.             if (day === 0 || day === 6) {
  13.                 violations.push({ logId: record.id, logDate: logDate, user: record.user ? record.user.name : "Unknown", day: day === 0 ? "Sunday" : "Saturday", reason: "Cannot log time on weekends" });
  14.             }
  15.         }

  16.         if (violations.length > 0) {
  17.             throw new ScriptError(JSON.stringify({ violations: violations }));
  18.         }
  19.     } catch (err) {
  20.         throw new ScriptError(err);
  21.     }
  22. }

Restrict Time Logs on Holidays 

Block time entries on organization holidays by retrieving holiday information from Zoho People and validating the selected log date.
Code
  1. function main() {
  2.     try {
  3.         let peopleUrl = "https://people.zoho.com";
  4.         let records = current.record;
  5.         let violations = [];

  6.         let monthMap = {"Jan":"01","Feb":"02","Mar":"03","Apr":"04","May":"05","Jun":"06","Jul":"07","Aug":"08","Sep":"09","Oct":"10","Nov":"11","Dec":"12"};

  7.         function peopleDateToISO(d) {
  8.             let p = d.split("-");
  9.             if (p.length !== 3) return d;
  10.             return p[2] + "-" + (monthMap[p[1]] || "01") + "-" + (p[0].length === 1 ? "0" + p[0] : p[0]);
  11.         }

  12.         let years = {};
  13.         for (let i = 0; i < records.length; i++) {
  14.             if (records[i].log_date) years[records[i].log_date.substring(0, 4)] = true;
  15.         }

  16.         let holidaySet = {};
  17.         let holidayNames = {};
  18.         let yearKeys = Object.keys(years);
  19.         for (let y = 0; y < yearKeys.length; y++) {
  20.             let year = yearKeys[y];
  21.             let req = new HttpRequest();
  22.             req.url(`${peopleUrl}/people/api/leave/v2/holidays/get?from=01-Jan-${year}&to=31-Dec-${year}`);
  23.             req.method("GET");
  24.             req.connection("zpeople");
  25.             let holidays = req.execute().asJson().statusMessage.responseText.data || [];

  26.             for (let i = 0; i < holidays.length; i++) {
  27.                 let isoDate = peopleDateToISO(holidays[i].Date);
  28.                 holidaySet[isoDate] = true;
  29.                 holidayNames[isoDate] = holidays[i].Name;
  30.             }
  31.         }

  32.         for (let i = 0; i < records.length; i++) {
  33.             let record = records[i];
  34.             let logDate = record.log_date;
  35.             if (!logDate) continue;

  36.             if (holidaySet[logDate]) {
  37.                 violations.push({ logId: record.id, logDate: logDate, user: record.user ? record.user.name : "Unknown", holiday: holidayNames[logDate], reason: logDate + " is a holiday (" + holidayNames[logDate] + ")" });
  38.             }
  39.         }

  40.         if (violations.length > 0) {
  41.             throw new ScriptError(JSON.stringify({ violations: violations }));
  42.         }
  43.     } catch (err) {
  44.         throw new ScriptError(err);
  45.     }
  46. }

 Restrict Time Logs During Leave or Time Off

Restrict users from logging time on days when they are on approved leave or time off, using leave records from Zoho People.
Code
  1. function main() {
  2.     try {
  3.         let peopleUrl = "https://people.zoho.com";
  4.         let records = current.record;
  5.         let violations = [];

  6.         let monthMap = {"Jan":"01","Feb":"02","Mar":"03","Apr":"04","May":"05","Jun":"06","Jul":"07","Aug":"08","Sep":"09","Oct":"10","Nov":"11","Dec":"12"};

  7.         function peopleDateToISO(d) {
  8.             let p = d.split("-");
  9.             if (p.length !== 3) return d;
  10.             return p[2] + "-" + (monthMap[p[1]] || "01") + "-" + (p[0].length === 1 ? "0" + p[0] : p[0]);
  11.         }

  12.         function isoToPeopleDate(d) {
  13.             let p = d.split("-");
  14.             let m = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
  15.             return p[2] + "-" + m[parseInt(p[1]) - 1] + "-" + p[0];
  16.         }

  17.         let minDate = null;
  18.         let maxDate = null;
  19.         let uniqueUsers = {};

  20.         for (let i = 0; i < records.length; i++) {
  21.             let record = records[i];
  22.             let logDate = record.log_date;
  23.             if (logDate) {
  24.                 if (!minDate || logDate < minDate) minDate = logDate;
  25.                 if (!maxDate || logDate > maxDate) maxDate = logDate;
  26.             }
  27.             if (record.user && record.user.zuid) {
  28.                 uniqueUsers[String(record.user.zuid)] = true;
  29.             }
  30.         }

  31.         if (!minDate) return;

  32.         let req = new HttpRequest();
  33.         req.url(`${peopleUrl}/api/v2/leavetracker/leaves/records?from=${encodeURIComponent(isoToPeopleDate(minDate))}&to=${encodeURIComponent(isoToPeopleDate(maxDate))}`);
  34.         req.method("GET");
  35.         req.connection("zpeople");
  36.         let leaveRecordsMap = req.execute().asJson().statusMessage.responseText.records || {};
  37.         let leaveIds = Object.keys(leaveRecordsMap);

  38.         let userLeaveDayMap = {};
  39.         for (let i = 0; i < leaveIds.length; i++) {
  40.             let lr = leaveRecordsMap[leaveIds[i]];
  41.             let zuid = String(lr.ZUID);
  42.             if (!uniqueUsers[zuid]) continue;

  43.             let days = lr.Days || {};
  44.             let dayKeys = Object.keys(days);
  45.             for (let d = 0; d < dayKeys.length; d++) {
  46.                 let leaveCount = parseFloat(days[dayKeys[d]].LeaveCount) || 0;
  47.                 if (leaveCount <= 0) continue;
  48.                 let isoDate = peopleDateToISO(dayKeys[d]);
  49.                 if (!userLeaveDayMap[zuid]) userLeaveDayMap[zuid] = {};
  50.                 userLeaveDayMap[zuid][isoDate] = { leaveType: lr.Leavetype || "", status: lr.ApprovalStatus || "" };
  51.             }
  52.         }

  53.         for (let i = 0; i < records.length; i++) {
  54.             let record = records[i];
  55.             let logDate = record.log_date;
  56.             if (!logDate) continue;
  57.             let recordZuid = record.user ? String(record.user.zuid) : "";
  58.             if (!recordZuid) continue;

  59.             let userDays = userLeaveDayMap[recordZuid];
  60.             if (!userDays) continue;
  61.             let dayLeave = userDays[logDate];
  62.             if (!dayLeave) continue;

  63.             violations.push({ logId: record.id, logDate: logDate, user: record.user.name, leaveType: dayLeave.leaveType, status: dayLeave.status, reason: "User is on " + dayLeave.leaveType + " (" + dayLeave.status + ")" });
  64.         }

  65.         if (violations.length > 0) {
  66.             throw new ScriptError(JSON.stringify({ violations: violations }));
  67.         }
  68.     } catch (err) {
  69.         throw new ScriptError(err);
  70.     }
  71. }

 Allow Time Logs Only for Task or Issue Owners

Ensure that only users assigned as owners of a task or issue can log time against it. Unassigned tasks and unauthorized users are blocked.
Code
  1. function main() {
  2.     try {

  3.         let url = "https://projects.zoho.com";
  4.         let records = current.record;
  5.         let violations = [];

  6.         let taskLogs = [];
  7.         let projectTaskMap = {};

  8.         for (let i = 0; i < records.length; i++) {
  9.             let record = records[i];
  10.             let linked = record.module_detail;
  11.             if (!linked || linked.name === "general") continue;

  12.             taskLogs.push(record);
  13.             let projId = String(record.project_id);
  14.             if (!projectTaskMap[projId]) projectTaskMap[projId] = {};
  15.             projectTaskMap[projId][linked.id] = true;
  16.         }

  17.         if (taskLogs.length === 0) return;

  18.         let allTasks = [];
  19.         let projectIds = Object.keys(projectTaskMap);

  20.         if (projectIds.length === 1) {
  21.             let projId = projectIds[0];
  22.             let taskIds = Object.keys(projectTaskMap[projId]);
  23.             let filter = JSON.stringify({ criteria: [{ field_name: "id", criteria_condition: "contains", value: taskIds }], pattern: "1" });
  24.             let req = new HttpRequest();
  25.             req.url(`${url}/api/v3/portal/${current.org.zohoOrgId}/projects/${projId}/tasks?page=1&per_page=200&filter=${encodeURIComponent(filter)}`);
  26.             req.method("GET");
  27.             req.connection("zprojects");
  28.             allTasks = req.execute().asJson().statusMessage.responseText.tasks || [];
  29.         } else {
  30.             let uniqueMap = {};
  31.             for (let i = 0; i < projectIds.length; i++) {
  32.                 let ids = Object.keys(projectTaskMap[projectIds[i]]);
  33.                 for (let j = 0; j < ids.length; j++) uniqueMap[ids[j]] = true;
  34.             }
  35.             let filter = JSON.stringify({ criteria: [{ field_name: "id", criteria_condition: "contains", value: Object.keys(uniqueMap) }], pattern: "1" });
  36.             let req = new HttpRequest();
  37.             req.url(`${url}/api/v3/portal/${current.org.zohoOrgId}/tasks?page=1&per_page=200&filter=${encodeURIComponent(filter)}`);
  38.             req.method("GET");
  39.             req.connection("zprojects");
  40.             allTasks = req.execute().asJson().statusMessage.responseText.tasks || [];

  41.             let returnedIds = {};
  42.             for (let i = 0; i < allTasks.length; i++) returnedIds[String(allTasks[i].id)] = true;

  43.             let missingByProject = {};
  44.             for (let i = 0; i < projectIds.length; i++) {
  45.                 let projId = projectIds[i];
  46.                 let taskIds = Object.keys(projectTaskMap[projId]);
  47.                 for (let j = 0; j < taskIds.length; j++) {
  48.                     if (!returnedIds[taskIds[j]]) {
  49.                         if (!missingByProject[projId]) missingByProject[projId] = [];
  50.                         missingByProject[projId].push(taskIds[j]);
  51.                     }
  52.                 }
  53.             }

  54.             let missingProjIds = Object.keys(missingByProject);
  55.             for (let i = 0; i < missingProjIds.length && i < 2; i++) {
  56.                 let projId = missingProjIds[i];
  57.                 let mFilter = JSON.stringify({ criteria: [{ field_name: "id", criteria_condition: "contains", value: missingByProject[projId] }], pattern: "1" });
  58.                 let mReq = new HttpRequest();
  59.                 mReq.url(`${url}/api/v3/portal/${current.org.zohoOrgId}/projects/${projId}/tasks?page=1&per_page=200&filter=${encodeURIComponent(mFilter)}`);
  60.                 mReq.method("GET");
  61.                 mReq.connection("zprojects");
  62.                 let moreTasks = mReq.execute().asJson().statusMessage.responseText.tasks || [];
  63.                 for (let k = 0; k < moreTasks.length; k++) allTasks.push(moreTasks[k]);
  64.             }
  65.         }

  66.         let taskOwnerMap = {};
  67.         for (let i = 0; i < allTasks.length; i++) {
  68.             let task = allTasks[i];
  69.             let owners = task.owners_and_work.owners || [];
  70.             let realZpuids = [];
  71.             for (let j = 0; j < owners.length; j++) {
  72.                 if (owners[j].zuid && owners[j].zuid !== 0) realZpuids.push(String(owners[j].zpuid));
  73.             }
  74.             taskOwnerMap[String(task.id)] = realZpuids;
  75.         }

  76.         for (let i = 0; i < taskLogs.length; i++) {
  77.             let record = taskLogs[i];
  78.             let taskId = String(record.module_detail.id);
  79.             let userZpuid = record.user ? String(record.user.id) : String(current.user.id);
  80.             let ownerZpuids = taskOwnerMap[taskId];

  81.             if (!ownerZpuids) continue;

  82.             if (ownerZpuids.length === 0) {
  83.                 violations.push({ logId: record.id, taskId: taskId, user: record.user ? record.user.name : "Unknown", reason: "Task is unassigned" });
  84.             } else if (ownerZpuids.indexOf(userZpuid) === -1) {
  85.                 violations.push({ logId: record.id, taskId: taskId, user: record.user ? record.user.name : "Unknown", reason: "User is not the task owner" });
  86.             }
  87.         }

  88.         if (violations.length > 0) {
  89.             throw new ScriptError(JSON.stringify({ violations: violations }));
  90.         }
  91.     } catch (err) {
  92.         throw new ScriptError(err);
  93.     }
  94. }

Restrict Time Logs for Tasks in Review

Restrict users from logging time against tasks whose status is In Review, ensuring work is logged only during active execution stages.
Code
  1. function main() {
  2.     try {
  3.         
  4.         let url = " https://projects.zoho.com";
  5.         let records = current.record;
  6.         let violations = [];

  7.         let taskLogs = [];
  8.         let projectTaskMap = {};

  9.         for (let i = 0; i < records.length; i++) {
  10.             let record = records[i];
  11.             let linked = record.module_detail;
  12.             if (!linked || linked.name !== "Task") continue;

  13.             taskLogs.push(record);
  14.             let projId = String(record.project_id);
  15.             if (!projectTaskMap[projId]) projectTaskMap[projId] = {};
  16.             projectTaskMap[projId][linked.id] = true;
  17.         }

  18.         if (taskLogs.length === 0) return;

  19.         let allTasks = [];
  20.         let projectIds = Object.keys(projectTaskMap);

  21.         if (projectIds.length === 1) {
  22.             let projId = projectIds[0];
  23.             let taskIds = Object.keys(projectTaskMap[projId]);
  24.             let filter = JSON.stringify({ criteria: [{ field_name: "id", criteria_condition: "contains", value: taskIds }], pattern: "1" });
  25.             let req = new HttpRequest();
  26.             req.url(`${url}/api/v3/portal/${current.org.zohoOrgId}/projects/${projId}/tasks?page=1&per_page=200&filter=${encodeURIComponent(filter)}`);
  27.             req.method("GET");
  28.             req.connection("zprojects");
  29.             allTasks = req.execute().asJson().statusMessage.responseText.tasks || [];
  30.         } else {
  31.             let uniqueMap = {};
  32.             for (let i = 0; i < projectIds.length; i++) {
  33.                 let ids = Object.keys(projectTaskMap[projectIds[i]]);
  34.                 for (let j = 0; j < ids.length; j++) uniqueMap[ids[j]] = true;
  35.             }
  36.             let filter = JSON.stringify({ criteria: [{ field_name: "id", criteria_condition: "contains", value: Object.keys(uniqueMap) }], pattern: "1" });
  37.             let req = new HttpRequest();
  38.             req.url(`${url}/api/v3/portal/${current.org.zohoOrgId}/tasks?page=1&per_page=200&filter=${encodeURIComponent(filter)}`);
  39.             req.method("GET");
  40.             req.connection("zprojects");
  41.             allTasks = req.execute().asJson().statusMessage.responseText.tasks || [];

  42.             let returnedIds = {};
  43.             for (let i = 0; i < allTasks.length; i++) returnedIds[String(allTasks[i].id)] = true;

  44.             let missingByProject = {};
  45.             for (let i = 0; i < projectIds.length; i++) {
  46.                 let projId = projectIds[i];
  47.                 let taskIds = Object.keys(projectTaskMap[projId]);
  48.                 for (let j = 0; j < taskIds.length; j++) {
  49.                     if (!returnedIds[taskIds[j]]) {
  50.                         if (!missingByProject[projId]) missingByProject[projId] = [];
  51.                         missingByProject[projId].push(taskIds[j]);
  52.                     }
  53.                 }
  54.             }

  55.             let missingProjIds = Object.keys(missingByProject);
  56.             for (let i = 0; i < missingProjIds.length && i < 2; i++) {
  57.                 let projId = missingProjIds[i];
  58.                 let mFilter = JSON.stringify({ criteria: [{ field_name: "id", criteria_condition: "contains", value: missingByProject[projId] }], pattern: "1" });
  59.                 let mReq = new HttpRequest();
  60.                 mReq.url(`${url}/api/v3/portal/${current.org.zohoOrgId}/projects/${projId}/tasks?page=1&per_page=200&filter=${encodeURIComponent(mFilter)}`);
  61.                 mReq.method("GET");
  62.                 mReq.connection("zprojects");
  63.                 let moreTasks = mReq.execute().asJson().statusMessage.responseText.tasks || [];
  64.                 for (let k = 0; k < moreTasks.length; k++) allTasks.push(moreTasks[k]);
  65.             }
  66.         }

  67.         let taskStatusMap = {};
  68.         let taskNameMap = {};
  69.         for (let i = 0; i < allTasks.length; i++) {
  70.             let task = allTasks[i];
  71.             let id = String(task.id);
  72.             taskStatusMap[id] = task.status;
  73.             taskNameMap[id] = task.name;
  74.         }

  75.         for (let i = 0; i < taskLogs.length; i++) {
  76.             let record = taskLogs[i];
  77.             let taskId = String(record.module_detail.id);
  78.             let status = taskStatusMap[taskId];
  79.             if (!status) continue;

  80.             if (status.name === 'In Review') {
  81.                 violations.push({ logId: record.id, taskId: taskId, taskName: taskNameMap[taskId], status: status.name, reason: "Task is in review" });
  82.             }
  83.         }

  84.         if (violations.length > 0) {
  85.             throw new ScriptError(JSON.stringify({ violations: violations }));
  86.         }
  87.     } catch (err) {
  88.         throw new ScriptError(err);
  89.     }
  90. }

 Restrict General Time Logs

Block general time logs that are not linked to a task or issue, ensuring every time entry is associated with a work item.
Code
  1. function main() {
  2.     try {
  3.         let records = current.record;
  4.         let violations = [];

  5.         for (let i = 0; i < records.length; i++) {
  6.             let record = records[i];
  7.             let linked = record.module_detail;

  8.             if (linked && linked.name === "general") {
  9.                 violations.push({ logId: record.id, user: record.user ? record.user.name : "Unknown", reason: "General timelogs not allowed. Must link to a Task or Issue." });
  10.             }
  11.         }

  12.         if (violations.length > 0) {
  13.             throw new ScriptError(JSON.stringify({ violations: violations }));
  14.         }
  15.     } catch (err) {
  16.         throw new ScriptError(err);
  17.     }
  18. }

 Restrict Time Logs to the Current Day

Allow time logging only for the current date. The script blocks both backdated and future-dated time entries.
Code
  1. function main() {
  2.     try {
  3.         let records = current.record;
  4.         let violations = [];

  5.         let now = new Date();
  6.         let todayStr = now.getFullYear() + "-" +
  7.             (now.getMonth() + 1 < 10 ? "0" : "") + (now.getMonth() + 1) + "-" +
  8.             (now.getDate() < 10 ? "0" : "") + now.getDate();

  9.         for (let i = 0; i < records.length; i++) {
  10.             let record = records[i];
  11.             let logDate = record.log_date;
  12.             if (!logDate) continue;

  13.             if (logDate !== todayStr) {
  14.                 violations.push({ logId: record.id, logDate: logDate, today: todayStr, user: record.user ? record.user.name : "Unknown", reason: logDate < todayStr ? "Cannot log for past dates" : "Cannot log for future dates" });
  15.             }
  16.         }

  17.         if (violations.length > 0) {
  18.             throw new ScriptError(JSON.stringify({ violations: violations }));
  19.         }
  20.     } catch (err) {
  21.         throw new ScriptError(err);
  22.     }
  23. }

 Restrict Time Logs for Tasks

Restrict users from logging time against tasks. This can be used in scenarios where time logging is permitted only for issues or general logs.
Code
  1. function main() {
  2.     try {
  3.         let records = current.record;
  4.         let violations = [];

  5.         for (let i = 0; i < records.length; i++) {
  6.             let record = records[i];
  7.             let linked = record.module_detail;

  8.             if (linked && linked.name === "Task") {
  9.                 violations.push({ logId: record.id, user: record.user ? record.user.name : "Unknown", reason: "Logging time against Tasks is not allowed" });
  10.             }
  11.         }

  12.         if (violations.length > 0) {
  13.             throw new ScriptError(JSON.stringify({ violations: violations }));
  14.         }
  15.     } catch (err) {
  16.         throw new ScriptError(err);
  17.     }
  18. }

        Create. Review. Publish.

        Write, edit, collaborate on, and publish documents to different content management platforms.

        Get Started Now


          Access your files securely from anywhere

            Zoho CRM Training Programs

            Learn how to use the best tools for sales force automation and better customer engagement from Zoho's implementation specialists.

            Zoho CRM Training
              Redefine the way you work
              with Zoho Workplace

                Zoho DataPrep Personalized Demo

                If you'd like a personalized walk-through of our data preparation tool, please request a demo and we'll be happy to show you how to get the best out of Zoho DataPrep.

                Zoho CRM Training

                  Create, share, and deliver

                  beautiful slides from anywhere.

                  Get Started Now


                    Zoho Sign now offers specialized one-on-one training for both administrators and developers.

                    BOOK A SESSION







                                Quick LinksWorkflow AutomationData Collection
                                Web FormsEnterpriseOnline Data Collection Tool
                                Embeddable FormsBankingBegin Data Collection
                                Interactive FormsWorkplaceData Collection App
                                CRM FormsCustomer ServiceAccessible Forms
                                Digital FormsMarketingForms for Small Business
                                HTML FormsEducationForms for Enterprise
                                Contact FormsE-commerceForms for any business
                                Lead Generation FormsHealthcareForms for Startups
                                Wordpress FormsCustomer onboardingForms for Small Business
                                No Code FormsConstructionRSVP tool for holidays
                                Free FormsTravelFeatures for Order Forms
                                Prefill FormsNon-Profit

                                Intake FormsLegal
                                Mobile App
                                Form DesignerHR
                                Mobile Forms
                                Card FormsFoodOffline Forms
                                Assign FormsPhotographyMobile Forms Features
                                Translate FormsReal EstateKiosk in Mobile Forms
                                Electronic Forms
                                Drag & drop form builder

                                Notification Emails for FormsAlternativesSecurity & Compliance
                                Holiday FormsGoogle Forms alternative GDPR
                                Form to PDFJotform alternativeHIPAA Forms
                                Email FormsFormstack alternativeEncrypted Forms

                                Wufoo alternativeSecure Forms

                                TypeformWCAG

                                  Zoho FSM Video Tutorials


                                        All-in-one knowledge management and training platform for your employees and customers.

                                                  Create. Review. Publish.

                                                  Write, edit, collaborate on, and publish documents to different content management platforms.

                                                  Get Started Now




                                                                    You are currently viewing the help pages of Qntrl’s earlier version. Click here to view our latest version—Qntrl 3.0's help articles.




                                                                        Manage your brands on social media


                                                                          • Desk Community Learning Series


                                                                          • Digest


                                                                          • Functions


                                                                          • Meetups


                                                                          • Kbase


                                                                          • Resources


                                                                          • Glossary


                                                                          • Desk Marketplace


                                                                          • MVP Corner


                                                                          • Word of the Day


                                                                          • Ask the Experts


                                                                            Zoho Sheet Resources

                                                                             

                                                                                Zoho Forms Resources


                                                                                  Secure your business
                                                                                  communication with Zoho Mail


                                                                                  Mail on the move with
                                                                                  Zoho Mail mobile application

                                                                                    Stay on top of your schedule
                                                                                    at all times


                                                                                    Carry your calendar with you
                                                                                    Anytime, anywhere




                                                                                          Zoho Sign Resources

                                                                                            Sign, Paperless!

                                                                                            Sign and send business documents on the go!

                                                                                            Get Started Now




                                                                                                    Zoho TeamInbox Resources





                                                                                                              Zoho DataPrep Demo

                                                                                                              Get a personalized demo or POC

                                                                                                              REGISTER NOW


                                                                                                                Design. Discuss. Deliver.

                                                                                                                Create visually engaging stories with Zoho Show.

                                                                                                                Get Started Now








                                                                                                                                    • Related Articles

                                                                                                                                    • Working with CodeX Scripts

                                                                                                                                      CodeX Scripts allows users to define custom logic that runs automatically based on events in the default modules such as when a project, task and time log is created, updated, or deleted. These scripts help enforce validations, restrict actions, or ...
                                                                                                                                    • CodeX Scripts - Intro

                                                                                                                                      CodeX Scripts let you define custom logic that runs automatically when users interact with default modules such as tasks, projects, time logs. It allows users to control how the module behaves based on certain conditions. Users can write scripts ...
                                                                                                                                    • CodeX Scripts for Projects

                                                                                                                                      CodeX Scripts in Zoho Projects are advanced conditional logic scripts that help enforce specific rules and validations. Users can control how and when a project can be created, edited, or transitioned. This ensures better compliance and process ...
                                                                                                                                    • CodeX Scripts for Tasks

                                                                                                                                      CodeX Scripts in Zoho Projects are advanced conditional logic scripts that help enforce specific rules and validations. Users can control how and when tasks can be created, edited, or transitioned. This ensures better compliance and process ...
                                                                                                                                    • Time Logs List View

                                                                                                                                      Projects involve multiple activities by a number of people. These activities are time-sensitive and must be monitored to keep costs and timelines on track. Time logs are used to track the amount of time spent on various activities. Users can log time ...
                                                                                                                                      Wherever you are is as good as
                                                                                                                                      your workplace

                                                                                                                                        Resources

                                                                                                                                        Videos

                                                                                                                                        Watch comprehensive videos on features and other important topics that will help you master Zoho CRM.



                                                                                                                                        eBooks

                                                                                                                                        Download free eBooks and access a range of topics to get deeper insight on successfully using Zoho CRM.



                                                                                                                                        Webinars

                                                                                                                                        Sign up for our webinars and learn the Zoho CRM basics, from customization to sales force automation and more.



                                                                                                                                        CRM Tips

                                                                                                                                        Make the most of Zoho CRM with these useful tips.



                                                                                                                                          Zoho Show Resources