How would you improve this lead life cycle scheduled function?

How would you improve this lead life cycle scheduled function?

void schedule.IntegratedLeadProcessing()
{
    // STEP 1: Retrieve Leads from CRM
    leads = zoho.crm.getRecords("Leads",1,200);
    if(leads == null || leads.size() == 0)
    {
        return;
    }

    // STEP 2: Process Timeline Engagement & Update Lead Status
    currentDate = zoho.currentdate.toDate();
    thirtyDaysAgo = currentDate.addDay(-30);

    leadIds = list();
    for each  lead in leads
    {
        if(lead.get("id") != null)
        {
            leadIds.add(lead.get("id"));
        }
    }

    timelineMap = Map();
    for each  leadId in leadIds
    {
        timelineURL = "https://www.zohoapis.com/crm/v7/Leads/" + leadId + "/__timeline?sort_by=audited_time" + "&include_inner_details=field_history.data_type,field_history.field_label,field_history.enable_colour_code,field_history.pick_list_values,done_by.type__s,done_by.profile";
        timelineResponse = invokeurl
        [
            url :timelineURL
            type :GET
            connection:"zoho_crm"
        ];
        timelineList = list();
        if(timelineResponse.containsKey("__timeline"))
        {
            timelineList = timelineResponse.get("__timeline");
        }
        timelineMap.put(leadId,timelineList);
    }

    leadEngagementMap = Map();
    desiredModules = list();
    desiredModules.add("calls");
    desiredModules.add("meetings");
    desiredModules.add("emails");
    desiredModules.add("tasks");

    for each  lead in leads
    {
        leadId = lead.get("id");
        leadName = ifnull(lead.get("Lead_Name"),ifnull(lead.get("Full_Name"),"Unknown Lead"));
        createdTimeStr = lead.get("Created_Time");
        if(createdTimeStr != null)
        {
            createdDate = createdTimeStr.toDate("yyyy-MM-dd'T'HH:mm:ssXXX");
        }
        else
        {
            createdDate = currentDate;
        }
        lastEngagementDate = null;
        engagementDates = list();

        leadTimeline = timelineMap.get(leadId);
        for each  event in leadTimeline
        {
            if(event.containsKey("audited_time") && event.containsKey("record"))
            {
                rec = event.get("record");
                moduleName = "";
                if(rec.containsKey("link_data"))
                {
                    linkData = rec.get("link_data");
                    if(linkData != null && linkData.size() > 0)
                    {
                        moduleName = linkData.get(0);
                    }
                }
                if(moduleName == "" && rec.containsKey("module") && rec.get("module").containsKey("api_name"))
                {
                    moduleName = rec.get("module").get("api_name");
                }
                if(moduleName == "" && event.containsKey("description"))
                {
                    description = event.get("description").toLowerCase();
                    if(description.contains("task"))
                    {
                        moduleName = "tasks";
                    }
                    else if(description.contains("call"))
                    {
                        moduleName = "calls";
                    }
                    else if(description.contains("meeting"))
                    {
                        moduleName = "meetings";
                    }
                    else if(description.contains("email"))
                    {
                        moduleName = "emails";
                    }
                }
                moduleNameLower = moduleName.toLowerCase();

                if(desiredModules.contains(moduleNameLower))
                {
                    try 
                    {
                        eventTimeStr = event.get("audited_time");
                        eventDate = eventTimeStr.toDate("yyyy-MM-dd'T'HH:mm:ssXXX");
                        engagementDates.add(eventDate);
                    }
                    catch (e)
                    {
                    }
                }
            }
        }

        if(engagementDates.size() > 0)
        {
            mostRecentEvent = engagementDates.get(0);
            for each  dt in engagementDates
            {
                if(dt.toLong() > mostRecentEvent.toLong())
                {
                    mostRecentEvent = dt;
                }
            }
            lastEngagementDate = mostRecentEvent;
        }
        else
        {
            lastEngagementDate = createdDate;
        }

        daysSinceEngagement = (currentDate.toLong() - lastEngagementDate.toLong()) / 86400000;
        if(engagementDates.size() > 0)
        {
            if(daysSinceEngagement <= 7)
            {
                leadStatus = "Hot";
            }
            else if(daysSinceEngagement <= 14)
            {
                leadStatus = "Warm";
            }
            else
            {
                leadStatus = "Cold";
            }
        }
        else
        {
            leadStatus = "Cold";
        }
        updateData = Map();
        updateData.put("Last_Engagement_Date",lastEngagementDate.toString("yyyy-MM-dd"));
        updateData.put("Lead_Status",leadStatus);
        leadEngagementMap.put(leadId,updateData);
    }

    for each  leadId in leadEngagementMap.keys()
    {
        updateData = leadEngagementMap.get(leadId);
        updateResp = zoho.crm.updateRecord("Leads",leadId,updateData);
    }

    // STEP 3: Apply Score Decay Adjustments
    scoringRules = list();
    scoringRules.add("Industry_Fit");
    scoringRules.add("Company_Size");
    scoringRules.add("Location_Priority");
    scoringRules.add("Intent");
    scoringRules.add("Buying_Group_Role");
    baseScores = Map();
    baseScores.put("Industry_Fit",0);
    baseScores.put("Company_Size",15);
    baseScores.put("Location_Priority",10);
    baseScores.put("Intent",0);
    baseScores.put("Buying_Group_Role",0);

    processedCount = 0;
    updatedCount = 0;
    for each  lead in leads
    {
        processedCount = processedCount + 1;
        leadId = lead.get("id");
        positiveTP = ifnull(lead.get("Positive_Touch_Points"),0).toLong();
        negativeTP = ifnull(lead.get("Negative_Touch_Points"),0).toLong();
        engaged = positiveTP > 0 && positiveTP > negativeTP;
        refDate = lead.get("Last_Engagement_Date");
        if(refDate != null)
        {
            refDate = refDate.toDate();
        }
        else
        {
            refDate = lead.get("Created_Time");
            if(refDate != null)
            {
                refDate = refDate.toDate();
            }
        }
        updateData = Map();
        if(engaged)
        {
            updateData.put("Score_Decay_Counter",0);
            updateData.put("Score_Decay_Reason","");
            for each  rule in scoringRules
            {
                currentScore = 0;
                try 
                {
                    currentScore = ifnull(lead.get(rule),"0").toDecimal();
                }
                catch (e)
                {
                    currentScore = 0;
                }
                baseScore = baseScores.get(rule).toDecimal();
                if(currentScore < baseScore)
                {
                    updateData.put(rule,baseScore);
                }
            }
        }
        else if(refDate != null)
        {
            daysInactive = (currentDate.toLong() - refDate.toLong()) / 86400000;
            if(daysInactive >= 14)
            {
                decayCounter = (daysInactive / 14).toLong();
                for each  rule in scoringRules
                {
                    currentScore = 0;
                    try 
                    {
                        currentScore = ifnull(lead.get(rule),"0").toDecimal();
                    }
                    catch (e)
                    {
                        currentScore = 0;
                    }
                    if(currentScore > 0)
                    {
                        updateData.put(rule,currentScore * 0.95);
                    }
                }
                updateData.put("Score_Decay_Counter",decayCounter);
                updateData.put("Score_Decay_Reason","Score reduced due to lack of engagement (" + daysInactive.toLong() + " days since last engagement)");
            }
        }
        if(updateData.size() > 0)
        {
            updateResponse = zoho.crm.updateRecord("Leads",leadId,updateData);
            updatedCount = updatedCount + 1;
        }
    }

    // STEP 4: Process Tagging for Leads
    getTagsResp = invokeurl
    [
        url :settingsUrl
        type :GET
        connection:"zoho_crm"
    ];
    existingTagsList = list();
    if(getTagsResp.containsKey("tags"))
    {
        existingTagsList = getTagsResp.get("tags");
    }

    plannedTags = list();
    plannedTags.add({"name":"Introduction","color_code":"#F17574"});
    plannedTags.add({"name":"Rapport Building","color_code":"#F48435"});
    plannedTags.add({"name":"Existing Business","color_code":"#E7A826"});
    plannedTags.add({"name":"Closing","color_code":"#A8C026"});
    plannedTags.add({"name":"0-5m/yr","color_code":"#63C57E"});
    plannedTags.add({"name":"5-50m/yr","color_code":"#1DB9B4"});
    plannedTags.add({"name":"50m-100m/yr","color_code":"#57B1FD"});
    plannedTags.add({"name":"100m+/yr","color_code":"#879BFC"});
    plannedTags.add({"name":"0-20 Users","color_code":"#D297EE"});
    plannedTags.add({"name":"20-60 Users","color_code":"#FD87BD"});
    plannedTags.add({"name":"60+ Users","color_code":"#969696"});
    plannedTags.add({"name":"Wichita (Local)","color_code":"#658BA8"});
    plannedTags.add({"name":"Mid West (Regional)","color_code":"#B88562"});
    plannedTags.add({"name":"National","color_code":"#F17574"});
    plannedTags.add({"name":"Data Incomplete","color_code":"#ECE81A"});

    for each  tagMap in plannedTags
    {
        plannedTagName = tagMap.get("name");
        tagExists = false;
        for each  existTag in existingTagsList
        {
            if(existTag.get("name") == plannedTagName)
            {
                tagExists = true;
                break;
            }
        }
        if(!tagExists)
        {
            inputJSON = {"tags":{{"name":plannedTagName,"color_code":tagMap.get("color_code")}}};
            createResp = invokeurl
            [
                url :createUrl
                type :POST
                parameters:inputJSON.toString()
                connection:"zoho_crm"
            ];
        }
    }

    updated_leads = 0;
    for each  lead in leads
    {
        leadId = lead.get("id");
        newTagsList = list();
        leadDetails = zoho.crm.getRecordById("Leads",leadId);
        existingTags = ifnull(leadDetails.get("Tag"),list());
        if(existingTags.size() > 0)
        {
            removeParam = Map();
            removeParam.put("ids",list(leadId));
            removeParam.put("tags",existingTags);
            removeResp = invokeurl
            [
                url :removeTagsUrl
                type :POST
                parameters:removeParam.toString()
                connection:"zoho_crm"
            ];
        }

        newTagsList.add("Data Incomplete");
        if(newTagsList.size() > 0)
        {
            param = Map();
            formattedTags = list();
            for each  tagName in newTagsList
            {
                formattedTags.add({"name":tagName});
            }
            param.put("tags",formattedTags);
            param.put("ids",list(leadId));
            tagResp = invokeurl
            [
                url :addTagsUrl
                type :POST
                parameters:param.toString()
                connection:"zoho_crm"
            ];
            updated_leads = updated_leads + 1;
        }
    }

    // STEP 5: Lead Routing & Conversion
    salesAgents = list();
    salesAgents.add({"id":"owner_id","name":"owner_name"});
    totalAgents = salesAgents.size();
    currentAgentIndex = 0;
    agentLeadCount = Map();
    for each  agent in salesAgents
    {
        agentLeadCount.put(agent.get("id"),{"name":agent.get("name"),"count":0});
    }

    allLeads = zoho.crm.getRecords("Leads",1,200);
    if(allLeads == null || allLeads.isEmpty())
    {
        return;
    }

    for each  lead in allLeads
    {
        leadOwner = lead.get("Owner");
        if(leadOwner != null)
        {
            ownerId = leadOwner.get("id");
            ownerName = leadOwner.get("name");
            if(agentLeadCount.containsKey(ownerId))
            {
                agentLeadCount.put(ownerId,{"name":ownerName,"count":agentLeadCount.get(ownerId).get("count") + 1});
            }
            else
            {
                agentLeadCount.put(ownerId,{"name":ownerName,"count":1});
            }
        }
    }

    assignedLeads = 0;
    for each  lead in allLeads
    {
        if(lead.get("Owner") == null)
        {
            assignedAgent = salesAgents.get(currentAgentIndex % totalAgents);
            updateData = Map();
            updateData.put("Owner",assignedAgent.get("id"));
            updateResp = zoho.crm.updateRecord("Leads",lead.get("id"),updateData);
            assignedLeads = assignedLeads + 1;
            if(agentLeadCount.containsKey(assignedAgent.get("id")))
            {
                agentLeadCount.put(assignedAgent.get("id"),{"name":assignedAgent.get("name"),"count":agentLeadCount.get(assignedAgent.get("id")).get("count") + 1});
            }
            else
            {
                agentLeadCount.put(assignedAgent.get("id"),{"name":assignedAgent.get("name"),"count":1});
            }
            currentAgentIndex = (currentAgentIndex + 1) % totalAgents;
        }
    }

    convertedLeads = 0;
    currentDate = zoho.currentdate.toDate();
    sevenDaysAgo = currentDate.addDay(-7);
    for each  lead in allLeads
    {
        leadId = lead.get("id");
        timeline = zoho.crm.getRelatedRecords("Activities","Leads",leadId);
        if(timeline.isEmpty())
        {
            continue;
        }
        activityFound = false;
        lastValidActivity = null;
        for each  activity in timeline
        {
            activityType = activity.get("Activity_Type");
            activityStatus = activity.get("Status");
            activityTime = activity.get("Created_Time").toDate();
            if((activityType == "Call" || activityType == "Meeting") && activityTime >= sevenDaysAgo)
            {
                lastValidActivity = activity;
                if(activityStatus == "Completed")
                {
                    activityFound = true;
                    break;
                }
            }
        }
        if(activityFound)
        {
            conversionMap = Map();
            dealList = list();
            dealList.add("Deals");
            dealList.add("Accounts");
            conversionMap.put("create",dealList);
            convertResp = zoho.crm.convertLead(leadId,conversionMap);
            convertedLeads = convertedLeads + 1;
            assignedAgent = salesAgents.get(currentAgentIndex % totalAgents);
            dealData = Map();
            dealData.put("Owner",assignedAgent.get("id"));
            dealUpdateResp = zoho.crm.updateRecord("Deals",convertResp.get("Deals"),dealData);
            currentAgentIndex = (currentAgentIndex + 1) % totalAgents;
        }
    }
}

    • Sticky Posts

    • Kaizen #198: Using Client Script for Custom Validation in Blueprint

      Nearing 200th Kaizen Post – 1 More to the Big Two-Oh-Oh! Do you have any questions, suggestions, or topics you would like us to cover in future posts? Your insights and suggestions help us shape future content and make this series better for everyone.
    • Kaizen #226: Using ZRC in Client Script

      Hello everyone! Welcome to another week of Kaizen. In today's post, lets see what is ZRC (Zoho Request Client) and how we can use ZRC methods in Client Script to get inputs from a Salesperson and update the Lead status with a single button click. In this
    • Kaizen #222 - Client Script Support for Notes Related List

      Hello everyone! Welcome to another week of Kaizen. The final Kaizen post of the year 2025 is here! With the new Client Script support for the Notes Related List, you can validate, enrich, and manage notes across modules. In this post, we’ll explore how
    • Kaizen #217 - Actions APIs : Tasks

      Welcome to another week of Kaizen! In last week's post we discussed Email Notifications APIs which act as the link between your Workflow automations and you. We have discussed how Zylker Cloud Services uses Email Notifications API in their custom dashboard.
    • Kaizen #216 - Actions APIs : Email Notifications

      Welcome to another week of Kaizen! For the last three weeks, we have been discussing Zylker's workflows. We successfully updated a dormant workflow, built a new one from the ground up and more. But our work is not finished—these automated processes are