When an order requires pausing or special handling, it often enters a "Hold" or "Grover" state. To prevent scheduling conflicts, inaccurate time tracking, and cluttered portals, our automated cleanup logic immediately steps in to reset or reassign these orders.
This page explains exactly what happens behind the scenes when an order is flagged for Hold or routed to Grover, and how the system safely cleans up the associated data.
Routing Logic Overview
Every time an order updates, the system checks the assigned Lead or Staff name. If it detects a special state, it "short-circuits" the standard routing and triggers a dedicated cleanup pipeline.
flowchart TD
A["Order Update Triggered"] --> B{"Check Assigned Staff"}
B -- "Hold" --> C["Hold Cleanup Pipeline"]
B -- "Grover" --> D["Grover Pipeline"]
B -- "Other Tech" --> E["Standard Time & Routing Logic"]
C --> F["Wipe assignments & reset times"]
D --> G["Assign to Grover & reset times"]The Admin Exception
In both the Hold and Grover pipelines, any subitems assigned to Admin are strictly preserved. The system will skip these rows to ensure administrative tasks and billing records remain intact.
1. The Hold Status Pipeline
When an order's lead or staff name is set to Hold, the system completely wipes the slate clean. This ensures no technicians are accidentally dispatched and no time is blocked off on the calendar.
Parent Item Updates
The main order (parent item) is scrubbed of its active routing data:
| Field | New Value | Purpose |
|---|---|---|
| Trigger | - (Cleared) | Stops any pending automations. |
| Instructions | Blank | Clears outdated routing notes. |
| Portal Sync | Empty | Removes the order from technician portals. |
| Services | Blank | Clears Charlie/Service summaries. |
| Total Mins | 0 | Frees up calendar capacity. |
| Calendar Color | Hold | Visually flags the order on the schedule. |
Subitem Updates
The system then loops through every service (subitem) attached to the order and applies the following resets:
- 1
Remove Staff Assignments
The assigned staff member is changed to None.
- 2
Unlock the Row
The lock status is changed to Unlocked, allowing it to be freely edited or reassigned later.
- 3
Reset Durations
The calculated minutes for the service are reset to 0.
- 4
Disconnect Portals
The subitem is removed from all individual technician portals.
2. The Grover Pipeline
"Grover" acts as a special holding or administrative state. When an order is assigned to Grover, the system doesn't just wipe the data—it actively reassigns ownership to the Grover user while resetting time calculations.
Parent Item Updates
| Field | New Value | Purpose |
|---|---|---|
| Staff | Grover | Officially assigns the parent order. |
| Trigger | - (Cleared) | Stops any pending automations. |
| Portal Sync | Grover's ID | Pushes the order to Grover's specific portal. |
| Total Mins | 0 | Ensures Grover's schedule isn't artificially blocked. |
| Calendar Color | Matches Deal Stage | Inherits the current Deal Stage color for context. |
Subitem Updates
Similar to the Hold pipeline, the system loops through all subitems (ignoring Admins) and routes them to Grover:
- 1
Assign to Grover
The staff column for the service is explicitly set to Grover.
- 2
Unlock the Row
The lock status is changed to Unlocked.
- 3
Reset Durations
The calculated minutes are zeroed out (
0). - 4
Sync to Grover's Portal
The subitem is linked directly to Grover's portal ID so it appears in their queue.
Need to resume an order?
Simply reassign the parent item from "Hold" or "Grover" to a standard technician (e.g., Matt, Kyle, Derek). The system will automatically exit these short-circuits and recalculate the required square footage, time minimums, and portal syncs.
Technical Reference
For developers maintaining the webhook logic, the cleanup pipelines utilize a short-circuit pattern in the Python execution environment.
Here is a simplified look at how the payload is constructed before being sent as a GraphQL mutation to Monday.com:
# Example: Hold Status Cleanup Payload Construction
if lead_name.lower() == "hold" or parent_staff.lower() == "hold":
hold_parent_v = {
COL_TRIGGER: {"label": "-"},
COL_INSTRUCTIONS: "",
COL_PORTAL_PARENT: {"item_ids": []},
COL_CHARLIE_SVCS: "",
COL_SERVICES_PARENT: "",
COL_TOTAL_MINS: "0",
COL_CAL_COLOR: {"label": "Hold"}
}
hold_sub_mutations = []
for h_idx, sub in enumerate(subitems):
# Skip Admin rows
if sub_staff.lower() == "admin":
continue
sub_update = {
C_SUB_STAFF: {"label": "None"},
C_SUB_LOCK: {"label": "Unlocked"},
C_SUB_MINS: "0",
C_SUB_PORTAL: {"item_ids": []}
}
# Append to mutation list...API Rate Limits
Because these pipelines loop through subitems and generate multiple GraphQL mutations (change_multiple_column_values), large orders with dozens of subitems will consume more API complexity points. The script batches these into a single request where possible to optimize performance.