Zoho FSM | Charge For Trips Taken and Time On-site

Charge For Trips Taken and Time On-site

Alert
Heads up! Zoho FSM now includes a native Job Costing feature! This custom implementation guide was originally provided as a workaround before the built-in feature was available.

If you have implemented this (or similar custom solutions), it is advisable to remove them before enabling the Job Costing feature. This will prevent any conflicts or unnecessary overlap between your custom setup and the new built-in functionality.

Use case: When creating an invoice from a work order, include charges for the trips (travel time or travel km) taken and the actual time on site.
 
Follow the steps below to implement this use case:
Step 1: Create parts for trip and on site charges
Step 2: Create a custom function
Step 3: Create a workflow rule

Step 1: Create parts for trip and on site charges

Create the following parts:
  1. Distance Travelled for Trip: Add a part to capture the charges for the distance travelled during a trip
  2. Billable Hours: Add a part to capture the charges for the time spent at the service location

Step 2: Create a custom function

Create a custom function to add to the parent work order of the service appointment two parts Distance Travelled for Trip, and Billable Hours. The Line Item Amount for these parts should be calculated as:
  1. For Distance Travelled for Trip
    (Distance travelled for all the trips in the service appointment) X (Unit Price of the part Distance Travelled for Trip)
  2. For Billable Hours
    (Total duration of all the time sheets in the service appointment) X (Unit Price of the part Billable Hours)
To create the custom function:
  1. Navigate to Setup > Automation > Actions.
  2. Select the Deluge Functions tab and click Create Function.
  3. Enter the following details and click Save:
    1. Function Name: CalculateTripAndOnsiteCharges
    2. Module: Service Appointments
    3. In the Deluge Editor, enter the following script:

      try 
      {

      erroravailable = false;

      errormessage = "";

      partRate = 0;

      travel_Amount = 0;

      final_Amount = 0;

      Amount = 0;

      Total_distance_travelled = 0;

      temp_hrs = 0;

      temp_min = 0;

      part_update_Map = Map();

      part_update_Map1 = Map();

      Time_Sheets_Resp = zoho.fsm.getRelatedRecords("Time_Sheets","Service_Appointments",service_appointment.get("id"));

      //info Time_Sheets_Resp;

      if(!Time_Sheets_Resp.isEmpty() && Time_Sheets_Resp != null)

      {

      Time_Sheets_List = Time_Sheets_Resp.get("data").toList();

      serv_line_item_id = Time_Sheets_Resp.get("data").toMap().get("Timesheet_X_Services").toMap().get("Service_Line_Item").toMap().get("id");

      for each  Time_Sheets_data in Time_Sheets_List

      {

      time = Time_Sheets_data.toMap().get("Duration");

      if(time != "" && time != null)

      {

      if(time.toNumber() >= 60)

      {

      temptime = time.toNumber() / 60;

      time = time.toNumber() - temptime.toNumber() * 60;

      temp_hrs = temp_hrs + temptime.toNumber();

      temp_min = temp_min + time;

      }

      else

      {

      temp_min = temp_min + time.toNumber();

      }

      }

      else

      {

      erroravailable = true;

      errormessage = "The time Sheet is yet be completed";

      }

      }

      new_hrs = temp_min / 60;

      new_final_hrs = temp_hrs + new_hrs;

      new_final_hrs = new_final_hrs.round(2);

      //info new_final_hrs;

      app_resp = zoho.fsm.getRecordById("Service_Appointments",service_appointment.get("id"));

      app_list = app_resp.get("data").toMap().get("Appointments_X_Services").toList();

      for each  app_data in app_list

      {

      if(app_data.toMap().get("Service_Line_Item").toMap().get("id") == serv_line_item_id)

      {

      wo_id = app_data.toMap().get("Work_Order").toMap().get("id");

      }

      }

      if(wo_id != null && wo_id != "")

      {

      wo_resp = zoho.fsm.getRecordById("Work_Orders",wo_id).toMap().get("data").toMap();

      tax = wo_resp.get("Service_Line_Items").toMap().get("Tax");

      taxRate = tax.get("Tax_Percentage");

      }

      respTrips = zoho.fsm.getRelatedRecords("Trips","Service_Appointments",service_appointment.get("id"));

      if(!respTrips.isNull() && !respTrips.isEmpty())

      {

      resp_data_list = respTrips.get("data").toList();

      //info resp_data_list;

      for each  resp_data in resp_data_list

      {

      appointment_name = resp_data.get("Appointment").toMap().get("name");

      trip_name = resp_data.get("Name");

      description = appointment_name + " " + trip_name;

      Distance_Travelled = resp_data.get("Distance_Travelled");

      //info Distance_Travelled;

      if(!Distance_Travelled.isNull() && !Distance_Travelled.isEmpty())

      {

      Total_distance_travelled = Total_distance_travelled + Distance_Travelled.get("value").toDecimal();

      }

      }

      }

      info Total_distance_travelled;

      partsList = zoho.fsm.getRecords("Service_And_Parts").get("data").toList();

      for each  parts_data in partsList

      {

      //info parts_data;

      if(parts_data.get("Name") == "Distance Travelled for Trip")

      {

      if(Total_distance_travelled > 0)

      {

      partRate = parts_data.get("Unit_Price");

      part_update_Map1.put("Part",parts_data.get("id"));

      part_update_Map1.put("Description",parts_data.get("Description"));

      part_update_Map1.put("Service_Line_Item",serv_line_item_id);

      part_update_Map1.put("Unit",parts_data.get("Unit"));

      part_update_Map1.put("List_Price",parts_data.get("Unit_Price"));

      part_update_Map1.put("Tax",tax);

      part_update_Map1.put("Quantity",Total_distance_travelled);

      }

      }

      if(parts_data.get("Name") == "Billable Hours")

      {

      part_update_Map.put("Part",parts_data.get("id"));

      part_update_Map.put("Description",parts_data.get("Description"));

      part_update_Map.put("Service_Line_Item",serv_line_item_id);

      part_update_Map.put("Quantity",new_final_hrs);

      part_update_Map.put("Unit",parts_data.get("Unit"));

      part_update_Map.put("List_Price",parts_data.get("Unit_Price"));

      part_update_Map.put("Tax",tax);

      }

      }

      Part_Line_Items_List = list();

      Part_Line_Items_List.add(part_update_Map);

      if(!part_update_Map1.isNull() && !part_update_Map1.isEmpty()  ) 

      {

      Part_Line_Items_List.add(part_update_Map1);

      }

      serv_update_Map = Map();

      serv_update_Map.put("id",serv_line_item_id);

      serv_update_Map.put("Part_Line_Items",Part_Line_Items_List);

      updateMapList = list();

      updateMapList.add(serv_update_Map);

      updateMap = Map();

      updateMap.put("Service_Line_Items",updateMapList);

      //info updateMap;

      }

      else

      {

      erroravailable = true;

      errormessage = "Time sheet is not created";

      }

      if(erroravailable != true)

      {

      info updateMap;

      result = zoho.fsm.updateRecord("Work_Orders",wo_id,updateMap);

      info result;

      }

      else

      {

      info errormessage;

      /* sendmail

      [

      from: zoho.adminuserid

      to: zoho.adminuserid

      subject: "Work Order is not updated"

      message: errormessage


      ] */

      }
      }
      catch (e)
      {

      info e;

      /* sendmail

      [

      from: zoho.adminuserid

      to: zoho.adminuserid

      subject: "Error in the function"

      message: e


      ] */
      }

Step 3: Create a workflow rule

Create a workflow rule to invoke the custom function CalculateTripAndOnsiteCharges when the status of the service appointment is Completed and the Billing Status of the service appointment is Not yet Invoiced.
  1. Navigate to Setup > Automation > Workflow Rules and click Create Workflow.
  2. Enter the following details, then click Next:
    1. Module: Service Appointments
    2. Rule Name: ChargeForTripsTakenAndTimeOnsite
    3. Description: When creating an invoice from a work order, include charges for the trips (travel time or travel km) taken and the actual time on site.



  3. Select the rule trigger as Edited. Select the checkbox Repeat this workflow whenever a Service Appointment is edited and add the condition Status is Completed. Click Next.



  4. Select the rule criteria as Only to the Service Appointment matching certain conditions add the condition Billing Status is Not yet Invoiced. Click Next.



  5. Click +Action and select Function.



  6. Select Existing Functions and click Next.



  7. Select the function created in the previous step.



  8. Click Save.

Testing the use case

  1. Create a work order.
  2. Create a service appointment for the work order.
  3. Start the service appointment.
  4. Create a trip for the service appointment.
  5. Complete Work for the service appointment.
    Ensure that the time sheets and trips have Start Time and End Time.