-
Notifications
You must be signed in to change notification settings - Fork 2
Transport Job Status Update Summary
This document explains how the status field is updated in Transport Job after the simplification to trigger-based updates only.
The status field in Transport Job is automatically updated through trigger-based hooks - no background jobs or polling. Status updates happen immediately when events occur.
The status field can have the following values:
- Draft - Initial state for new documents
- Submitted - Document has been submitted
- In Progress - At least one leg has started or been assigned
- Completed - All legs are completed or billed
- Cancelled - Document has been cancelled
-
Location:
transport_job.pyline 119-134 - When: Before document submission
-
Actions:
- Sets
_submittingflag to preventbefore_save()from callingupdate_status()during submission - Sets status to "Submitted" using direct SQL update (bypasses hooks)
- Commits the change immediately
- Sets
-
Purpose: Ensures status is set to "Submitted" before submission completes, even if
after_submit()doesn't run
-
Location:
transport_job.pyline 57-114 - When: Before saving the document (every save)
-
Actions:
- Checks database directly for submitted documents with Draft status and fixes them via SQL
- Skips status update if document is being submitted (flagged with
_submitting) - Calls
update_status()to calculate correct status - For submitted documents (docstatus = 1), if status changed, uses
db_set()to persist the change - Ensures status is never "Draft" for submitted documents (safeguard)
-
Why
db_set(): For submitted documents, we can't use regularsave()as it would trigger validation.db_set()updates the database directly.
-
Location:
transport_job.pyline 136-223 - When: Immediately after document submission
-
Actions:
- Verifies document is actually submitted (docstatus = 1)
- Clears
_submittingflag - Sets status to "Submitted" using direct SQL update (bypasses hooks)
- Verifies status was saved correctly, retries with
db_set()if needed - Reloads document to get latest state
- Calls
update_status()to check leg statuses - Updates status if legs are already in progress/completed
- Publishes realtime event
transport_job_status_changedif status changed - Final verification to ensure status is never "Draft" for submitted documents
- Purpose: Ensures status is set correctly on submission and reflects current leg states
-
Location:
transport_job.pyline 225-299 - When: After every save (including after submission)
-
Actions:
- Checks database directly for submitted documents with Draft status and fixes them via SQL
- For submitted documents, ensures status is correct based on leg statuses
- Calls
update_status()to recalculate status - Updates status via SQL if changed
- Publishes realtime event
transport_job_status_changedif status changed
-
Purpose: Catches cases where
after_submit()didn't run or status was reset, ensures status stays in sync
-
Location:
transport_job.pyline 301-324 - When: When document is cancelled
-
Actions:
- Gets previous status before cancellation
- Sets status to "Cancelled" via
db_set() - Commits the change
- Publishes realtime event
transport_job_status_changed - Releases capacity if vehicle was assigned
-
Location:
transport_leg.pyline 39-44 - When: After Transport Leg is saved (any change to leg)
-
Action: Calls
update_transport_job_status()(line 43)
-
Location:
transport_leg.pyline 280-398 -
When: Called from
TransportLeg.after_save()hook -
Actions:
- Checks database directly for current job status and docstatus
- Only updates if job is submitted (docstatus = 1)
- Fetches all leg statuses from database, using in-memory status for current leg
- Computes new status using same logic as
TransportJob.update_status() - Updates status via
frappe.db.set_value()if changed - Publishes realtime event
transport_job_status_changedif status changed - Logs status changes for debugging
-
Triggers: Transport Leg status changes due to:
- Setting
start_date→ Leg status becomes "Started" - Setting
end_date→ Leg status becomes "Completed" - Assigning
run_sheet→ Leg status becomes "Assigned" - Setting
sales_invoice→ Leg status becomes "Billed"
- Setting
The update_status() method (line 648-721 in transport_job.py) determines status based on:
- Sets status to "Draft" if not set
- Always sets status to "Cancelled"
- Gets all Transport Leg statuses from database
- Maps leg statuses to job status:
- All legs "Completed" or "Billed" → Job status = "Completed"
- Any leg "Started" or "Assigned" → Job status = "In Progress"
- All legs "Open" → Job status = "Submitted"
- Mixed statuses → Job status = "In Progress" (if any leg is in progress/completed)
- Ensures status is "Draft" if missing
- Doesn't override valid statuses during submission process
For submitted documents, status is updated using multiple methods:
-
Direct SQL Updates (used in
before_submit(),after_submit(),after_save()):- Bypasses all hooks and validation
- Ensures status is set even if hooks fail
- Used as safeguards to prevent "Draft" status on submitted documents
frappe.db.sql( f"UPDATE `tab{self.doctype}` SET `status` = 'Submitted' WHERE `name` = %s", (self.name,) ) frappe.db.commit()
-
db_set()Method (used inbefore_save(),on_cancel(),TransportLeg.update_transport_job_status()):- Updates database directly, bypassing validation
- Prevents recursive save loops and validation errors
- Used when status needs to change based on leg statuses
if self.docstatus == 1 and old_status != new_status and new_status: self.db_set("status", new_status, update_modified=False)
-
frappe.db.set_value()(used inTransportLeg.update_transport_job_status()):- Similar to
db_set()but called from external context - Updates database directly with better safety checks
frappe.db.set_value( "Transport Job", self.transport_job, "status", new_status, update_modified=False )
- Similar to
Why not use save()?
-
save()would trigger validation which might fail for submitted documents - Direct database updates bypass validation and prevent recursive save loops
The validate_status_transition() method (line 621-646) enforces these rules:
Allowed Transitions:
- Draft → Submitted, Cancelled
- Submitted → In Progress, Cancelled
- In Progress → Completed, Cancelled
- Completed → (no transitions allowed)
- Cancelled → (no transitions allowed)
Prevention Rules:
- Cannot cancel if Sales Invoice exists
Multiple safeguards ensure status is never "Draft" for submitted documents:
-
before_submit()Hook: Sets status to "Submitted" before submission completes -
before_save()Hook: Checks database and fixes Draft status on submitted documents via SQL -
after_submit()Hook: Sets status to "Submitted" via SQL, verifies it was saved, retries if needed -
after_save()Hook: Catches cases where status was reset to Draft and fixes it immediately -
Status Calculation:
update_status()ensures status is never "Draft" for docstatus = 1
These safeguards handle edge cases where hooks might not run or status might be reset.
When status changes to "Completed", _trigger_auto_billing() is called (line 709) to automatically create sales invoices if enabled.
Status changes are broadcast via realtime events to update client-side UI immediately:
-
Event Name:
transport_job_status_changed -
Published From:
-
after_submit()hook (line 196-204) -
after_save()hook (line 290-299) -
on_cancel()hook (line 312-320) -
TransportLeg.update_transport_job_status()(line 376-387)
-
-
Event Data: Includes
job_name,status,previous_status,docstatus, and optionallytriggered_byandleg_name - Purpose: Provides real-time UI updates without polling or manual refresh
Document Creation
↓
before_save() → update_status() → Sets "Draft"
↓
Document Submit
↓
before_submit() → Sets "Submitted" via SQL (safeguard)
↓
after_submit() → Sets "Submitted" via SQL → update_status() → May change to "In Progress" or "Completed"
↓
after_save() → Verifies status → update_status() if needed → Publishes realtime event
↓
Transport Leg Changes (start_date, end_date, run_sheet, sales_invoice)
↓
TransportLeg.after_save() → update_transport_job_status() → Computes status → Updates via db.set_value() → Publishes realtime event
↓
Document Save (if status changed)
↓
before_save() → Checks for Draft status on submitted docs → update_status() → db_set() if submitted and status changed
↓
after_save() → Verifies status → update_status() if needed → Publishes realtime event
-
Location:
transport_job.jsline 145-214 - When: For submitted documents (docstatus = 1)
-
Action: Listens for
transport_job_status_changedrealtime events -
Behavior:
- Updates document status immediately when event is received
- Triggers status change handlers (
on_status_in_progress,on_status_completed) - Refreshes status field in UI
- Cleans up listener when document is no longer submitted
- Purpose: Provides real-time UI updates without polling
-
Location:
transport_job.jsline 115-339 - For submitted documents, sets up realtime event listener
- For draft documents, fetches latest status from database if needed
- Updates UI to reflect current status
- No periodic polling - status is updated via realtime events or on form load/refresh
-
Location:
transport_job.jsline 641-716 - After submission, fetches latest status with retry mechanism (up to 10 retries)
- Ensures UI reflects server-side status changes
- Handles cases where Python
after_submit()hasn't completed yet
- ❌
update_transport_job_statuses()- Previously ran every 15 minutes - ❌
fix_stuck_transport_job_statuses()- Previously ran hourly - Reason: Status updates now happen immediately via triggers, making background jobs unnecessary
- ❌ 30-second interval status refresh in JavaScript
- Reason: Status updates happen in real-time via triggers, so polling is not needed
- Real-time Updates: Status updates happen immediately when events occur
- Simpler Architecture: No background jobs or polling to maintain
- More Reliable: Status is always in sync with actual document/leg states
- Better Performance: No unnecessary periodic checks
- Easier to Debug: Status changes are directly tied to specific events
If status is not updating correctly:
-
Check if document is submitted: Status updates for submitted documents use
db_set(), which requires docstatus = 1 - Check Transport Leg statuses: Job status is calculated from leg statuses
- Check Transport Leg linkage: Ensure legs are properly linked to the Transport Job
- Check for errors: Look for errors in logs when hooks are triggered
If needed, you can manually fix status using the whitelisted method:
frappe.call({
method: "logistics.transport.doctype.transport_job.transport_job.fix_submitted_job_status",
args: { job_name: "TRJ00000138" }
})The status field is updated through:
- ✅ Document lifecycle hooks (before_submit, before_save, after_submit, after_save, on_cancel)
- ✅ Transport Leg status changes (via after_save hook)
- ✅ Realtime events for immediate client-side updates
- ✅ Client-side JavaScript (realtime event listener, form refresh, no polling)
- ❌
Scheduled background tasks(removed) - ❌
Periodic polling(removed)
The status is automatically calculated based on Transport Leg statuses for submitted documents, ensuring the job status always reflects the current state of its legs. Multiple safeguards ensure status is never "Draft" for submitted documents, and realtime events provide immediate UI updates without polling.
Getting Started
- Getting Started
- Recent Platform Updates
- CargoNext v1 — Release Notes
- CargoNext v1 — Astraea Press Release
- Document Management
- Milestone Tracking
- Customer Portal
Setup and Settings
- Logistics Settings
- Credit Management
- Default Details and Relationships
- Sea Freight Settings
- Air Freight Settings
- Transport Settings
- Warehouse Settings
- Customs Settings
Sea Freight
- Sea Freight Module
- Sea Booking
- Sea Shipment
- Sea Consolidation
- Master Bill
- Shipper
- Consignee
- Container Type
- Container Management
Air Freight
Transport
- Transport Module
- Transport Order
- Transport Job
- Transport Consolidation
- Transport Leg
- Transport Plan
- Run Sheet
- Proof of Delivery
- Transport Template
- Load Type
- Transport Order — Inter-module Field Copy
Customs
Warehousing
- Warehousing Module
- Inbound Order
- Release Order
- Transfer Order
- VAS Order
- Stocktake Order
- Warehouse Job
- Warehouse Contract
- Gate Pass
- Periodic Billing
- Storage Location
- Handling Unit Type
Pricing Center
- Sales Quote
- Sales Quote — Separate Billings and Internal Job
- Change Request
- Sales Quote – Calculation Method
Job Management
- Job Management Module
- Revenue Recognition Policy — Accounts, Dates, and Charges
- Proforma GL Entries
- WIP and Accrual Reversal on Invoicing
Sustainability
Intercompany
Special Projects
Pages
Features
Reports
Glossary