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 alignment.
Refer here for CodeX Scripts for Projects.
CodeX Scripts are currently available as a limited release. To enable this feature for your portal, please contact support@zohoprojects.com Use Cases :
Create a Task
Creates a task using the selected layout and task list and update the task with necessary information.
- function main() {
- try {
- const layoutId = current.record.layoutId; // Choosen Layout Id
- const moduleId = current.record.moduleId; // Choosen Module Id
- const layout = client.getLayoutById(layoutId, moduleId);
- const newTask = new Record(layout);
- newTask.projectId = current.record.projectId;
- newTask.name = "Roles and responsib";
- newTask.tasklistId = "242942000000297017" // Tasklist ID
- const user1 = variables.user1.value; // ZPUID of user1 is stored in variables
- const user2 = variables.user2.value; // ZPUID of user2 is stored in variables
- const addedTask = client.save(newTask);
- addedTask.name = "Roles and responsibilities";
- addedTask.status = "242942000000040005"; // status Id based on layout. Here, 242942000000040005 corresponds to 'In Progress' status
- addedTask.description =
- "This is a task to define roles and responsibilities.";
- addedTask.start_date = "2025-12-09T12:00:00+05:30"; //start date should be proivded in ISO date and time formate
- addedTask.end_date = "2025-12-12T12:00:00+05:30"; // end date should be proivded in ISO date and time formate
- addedTask.owners_and_work = {
- work_unit: "hours_per_day", // Possible values: %, hours, %_per_day, hours_per_day
- total_work_value: "2160", // total work value based on work unit
- work_type: "standard_work", // Possible values: standard_work, flexible_work
- owners: [
- {
- owner: {
- id: user1,
- },
- work_value: "540", // work value assigned to the owner based on work unit
- },
- ],
- };
- addedTask.teams = ["242942000000090845", "242942000000090855"]; // Team IDs
- addedTask.completion_percentage = "50"; // Completion percentage value between 0 to 100
- addedTask.billing_type = "1"; // Possible values: 0 (None), 1 (Billable), 2 (Non-Billable)
- addedTask.single_line = "A single line custom field";
- addedTask.pick_list = "Option 1"; // Picklist option name
- addedTask.multi_line = "A multi line\ncustom field";
- addedTask.multi_select = ["Option 1", "Option 2", "Option 3"]; // Multi select picklist option names
- addedTask.user_pick_list_select_roles = "242942000000082031"; // Role ID
- addedTask.user_pick_list_all_users = user1;
- addedTask.user_pick_list_select_users = user2;
- addedTask.multi_user_pick_list_all_users = [user1, user2];
- addedTask.multi_user_pick_list_select_users = [user1, user2];
- addedTask.date_field = "2025-12-09"; // Date should be provided in ISO date format
- addedTask.date_time_field = "2025-12-09T12:00:00+05:30"; // Date time should be provided in ISO date and time format
- addedTask.checkbox_field = "true";
- addedTask.currency_field = "40000"; // Currency value
- addedTask.percentage_field = "25"; // Percentage value between 0 to 100
- addedTask.number_field = "450"; // Number value
- addedTask.decimal_field = "1.234"; // Decimal value
- addedTask.email_field = "zylker@zoho.com"; // Email address
- addedTask.phone_field = "1234567890"; // Phone number
- addedTask.url_field = "https://zylker.com/"; // URL
- client.save(addedTask);
- } catch (error) {
- console.log(error);
- throw new ScriptError(error); // Rethrow the error as a ScriptError which will be shown in Projects UI.
- }
- }
Restrict Task Creation When Project Baseline is set:
Restricts task creation if any baseline exists for the project to maintain the integrity of the project’s original plan.
Restrict Subtask Creation Beyond One Level
Restricts creating a subtask under another subtask to maintain a simple and manageable task hierarchy, allowing only one level of subtasks.
- function main() {
- if(current.record.parent_task !== current.record.root_task) {
- throw new ScriptError('Disable subtasks creation with more than 1 level');
- }
- }
-
- Restrict Task Completion with Open Issues
- Restricts closing a task if any associated issue is not in Closed or Canceled status to ensure all related issues are resolved.
-
- function main() {
-
- console.log('started');
- console.log(`${current.affectedFields}`);
-
- if (!current.affectedFields.status) {
- return;
- }
-
- const statusDtl = client.getStatusDetails(current.module.id, current.record.layoutId, current.affectedFields.status.newValue);
-
- if (!statusDtl.isClosedStatus) {
- return;
- }
-
- const task = current.record;
- const url = `https://projects.zoho.com/api/v3/portal/${current.org.zohoOrgId}/projects/${task.projectId}/tasks/${current.record.id}/associated-bugs`;
-
- const request = new HttpRequest();
- request.url(url);
- request.connection('zprojects');
- const response = request.execute();
- const associatedBugs = response.asJson()?.statusMessage?.responseText?.associated_bugs;
-
- if(!associatedBugs) {
- return;
- }
-
- for(let bug of associatedBugs) {
- if(bug.issue.status_name !== 'Closed' || bug.issue.status_name !== 'Cancelled') {
- throw new ScriptError('Open issues exist for this task');
- }
- }
-
- console.log(response.asJson()?.statusMessage?.responseText);
- }
Disable Task Creation in Projects with On-Hold status
Restricts task creation if the associated project is marked as On Hold.
- function main() {
-
- const projectId = current.record.projectId;
- const moduleItr = client.getModules();
-
- /**
- * @type {Module}
- */
- let module
-
- console.log('started');
- for (let mod of moduleItr) {
- console.log(`${mod}`);
- if (mod.name === 'Projects') {
- module = mod;
- }
- }
-
- if (!module) {
- return;
- }
-
- let projectDtl = client.getRecordById(module.id, projectId);
- let statusDtl = client.getStatusDetails(module.id, projectDtl.layoutId, projectDtl.status);
-
- if (statusDtl.name === 'zp.projstatus.onhold') {
- throw new ScriptError('Task Creation disabled for On Hold Projects');
- }
- }
-
Restrict Manual Date Change After Status Update
Restricts editing start date, end date, or duration if the status is not the layout’s default.
- function main() {
-
- //base condition #1
- if (!current.affectedFields.start_date
- && !current.affectedFields.end_date
- && !current.affectedFields.duration_value
- && !current.affectedFields.duration_unit) {
- return;
- }
-
- const statusDtl = client.getStatusDetails(current.module.id, current.record.layoutId, current.record.status);
- console.log(`${statusDtl}`);
-
- if (!statusDtl.isLayoutDefault) {
- throw new ScriptError('Start Date and End date change are only allowed in default status');
- }
- }
-
Restrict Task Closure if Open Subtasks Exist
Restricts the parent task's status to change to Closed if any open subtasks exists. This ensures that the parent task cannot be marked as complete until all its subtasks are closed.
-
- function main() {
-
- console.log(current.affectedFields.toString());
-
- //base condition handling
- if (!current.affectedFields?.status) {
- return;
- }
-
- const url = `https://projects.zoho.com/restapi/portal/${current.org.zohoOrgId}/projects/${current.record.projectId}/tasks/${current.record.id}/subtasks/`;
- let hasMore = true;
- const range = 200;
- let index = 1;
- console.log(url);
-
- while (hasMore) {
- let request = new HttpRequest();
- request.url(url);
- request.connection('zprojects');
- request.params({ index, range });
-
- const response = request.execute();
-
- console.log(response.asText());
- /**
- * @type {[]}
- */
- const tasks = response.asJson()?.statusMessage?.responseText?.tasks;
-
- //no subtasks present for current task
- if (!tasks || tasks.length == 0) {
- console.log('No subtasks exists for current task');
- return;
- }
-
- hasMore = tasks.length == range;
- index += range;
-
- console.log(tasks);
- console.log(tasks.length);
-
- for (let task of tasks) {
- console.log(task.completed);
- if (!task.completed) {
- throw new ScriptError('Open subtasks remain for this task.');
- }
- }
- }
-
-
- }
Restrict Status Change if Predecessors are Open
Restricts status updates if any of the predecessor tasks are incomplete to ensure dependency logic so that a successor task can only proceed once its predecessors are closed.
- function main() {
- console.log(current.affectedFields.toString());
-
- //base condition handling
- if(!current.affectedFields?.status) {
- return;
- }
- const url = `https://projects.zoho.com/restapi/portal/${current.org.zohoOrgId}/projects/${current.record.projectId}/tasks/${current.record.id}/`;
- console.log(url);
-
- let request = new HttpRequest();
- request.url(url);
- request.connection('zprojects');
- const response = request.execute();
- const taskDependencyDtl = response.asJson()?.statusMessage?.responseText?.tasks[0]?.dependency;
- //no dependency present in previous task
- if(!taskDependencyDtl || !taskDependencyDtl?.predecessor) {
- return;
- }
- console.log(taskDependencyDtl);
- for(let predecessorId of taskDependencyDtl.predecessor) {
- console.log(predecessorId);
- if(!taskDependencyDtl.dependencyDetails[predecessorId]?.IS_COMPLETED) {
- throw new ScriptError('Open predecessor tasks exists, please close them to proceed');
- }
- }
- }
- task_owner_status_update_restrict
- function main() {
- const task = current.record;
- const affectedFields = current.affectedFields;
- if(affectedFields.status) {
- if(!task._owners || !task._owners.includes(current.user.id)) {
- throw new ScriptError('Only task owner can edit the status');
- }
- }
- }
Restrict Subtask Creation
Restricts subtask creation to maintain a flat task structure.
- function main() {
-
- let task = current.record;
-
- /**
- *
- * @type Number
- */
- let startDate = task.start_date;
- Logger.info('Parent ID: ' + task.parent_task);
- Logger.info('Root Task ID: ' + task.root_task);
-
- // console.log(JSON.stringify(variables.blockSubTasks));
- if(task.parent_task || task.root_task) {
- Logger.info("Subtask creation is disabled");
- throw new ScriptError(JSON.stringify(variables.blockSubTasks.value));
- }
-
- //Similar case >>> Subtasks shouldn't be created under the closed parent task.
- }
-
Update Task Duration and Status Based on Conditions
Update task duration and status based on a specific custom field and current status.
For example, this script updates the duration of a task and its status when specific conditions are met:
- If the current status of the task is “On Hold”, it automatically changes the status to a specific ID (e.g., "In Progress").
- If a specific value is selected in a multi-select custom field, it sets the duration to 30 days.
- It also checks and logs the task’s billing type and affected fields.
- function main() {
-
- /**
- * @type {CurrentTask}
- */
- // const task = client.getRecordById('5000000000134', '5000000291025');
- // console.log(task.name);
- // throw new Error(task);
- //5000000179016 = corn
- let task = current.record;
- console.log(`id: ${task.id}`);
- console.log(current.affectedFields);
- console.log(`Task Billing type ${task.billing_type}`);
- console.log(`Task Biling Type BILLABLE ${TaskBillingType.BILLABLE}`);
- console.log(`Task Biling Type NON_BILLABLE ${TaskBillingType.NON_BILLABLE}`);
- console.log(`Task Biling Type NONE ${TaskBillingType.NONE}`);
-
- const statusDetails = client.getStatusDetails(task.moduleId, task.layoutId, task.status);
- if(statusDetails.name === 'On Hold') {
- task.status = '5000000000362';
- client.save(task);
- }
- console.log(JSON.stringify(statusDetails));
- if(task.weightage.includes("Medium") { //check if the multi select field is some options
- console.log('setting status to In Progress');
- task.duration_unit = TaskDurationUnit.DAYS;
- task.duration_value = 30;
- }
-
-
- }
Restrict Edits for Archived Projects
Restricts any update to a task if its project is archived to ensure that no task edits can be made under such projects.
- function main() {
- let task = current.record;
-
- let projectId = task.projectId;
- const moduleItr = client.getModules(0, 10);
- //projects module id: 5000000000380
- let moduleId;
- console.log(JSON.stringify(current.affectedFields));
- while(moduleItr.hasNext()) {
- let moduleObj = moduleItr.next();
- if(moduleObj.name == 'Projects') {
- moduleId = moduleObj.id;
- }
-
- }
- let project = client.getRecordById(moduleId, projectId);
-
- if(project._status == 'archived') {
- throw new ScriptError("Edits are disabled for archived projects");
- }
-
-
- }
-
Restricts Past Start Dates
Throws an error if the selected start date is in the past to maintain accurate schedules.
- function main() {
- let task = current.record;
- let date = Date.now();
- for(let key of Object.keys(current)) {
- console.log(key);
- }
-
- console.log('Checking current');
- console.log('Checking current.user');
- console.log(`current.user.zuid: ${current.user.zuid}`);
- console.log(`current.user.email ${current.user.email}`);
- console.log(`current.user.id ${current.user.id}`);
- console.log('checking current.module');
- console.log(`current.module.name ${current.module.name}`);
- console.log(`current.module.id ${current.module.id}`);
- console.log('checking org');
- console.log(`current.org.name ${current.org.zohoOrgId}`);
- console.log(`current.org.id ${current.org.orgId}`);
- console.log(`current.org.portalName ${current.org.portalName}`);
- console.log(`current.org.id ${current.org.name}`);
-
- /**********LOGIC IS BELOW HERE*************/
-
- const startDate = new Date(task.start_date)
- if(date > startDate) {
- console.log('Start date is in past date');
- throw new ScriptError('Start date cannot be in the past');
- }
- }
Restrict Editing Task on a Condition
Restricts any updates to the task if the checkbox field is enabled, except when changing the checkbox itself to prevent unintended changes.
- function main() {
-
- const affectedFields = current.affectedFields;
- const task = current.record;
-
- if(affectedFields && typeof affectedFields['checkbox_cf'] != 'undefined') {
- return; //Do nothing in case the checkbox itself is updated.
- }
-
- //boolean value for checkbox type field
- if(task.checkbox_cf) {
- throw new ScriptError('The check box is enabled, task is locked. ')
- }
- }
-
Got an idea for how you would like CodeX to work in your project setup? Drop your use case in the comments below!
More Reads