Skip to content

Transport Job Status Update Summary

Agilasoft Cloud Technologies edited this page Jan 20, 2026 · 2 revisions

This document explains how the status field is updated in Transport Job after the simplification to trigger-based updates only.

Overview

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.

Status Values

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

Status Update Triggers

1. Document Lifecycle Hooks

1.1 before_save() Hook

  • Location: transport_job.py line 46-58
  • When: Before saving the document (every save)
  • Action:
    1. Calls update_status() to calculate correct status
    2. For submitted documents (docstatus = 1), if status changed, uses db_set() to persist the change
  • Why db_set(): For submitted documents, we can't use regular save() as it would trigger validation. db_set() updates the database directly.

1.2 after_submit() Hook

  • Location: transport_job.py line 64-118
  • When: Immediately after document submission
  • Actions:
    1. Sets status to "Submitted" via db_set() (line 82)
    2. Calls update_status() to check leg statuses (line 102)
    3. Updates status if legs are already in progress/completed (line 108)
  • Purpose: Ensures status is set correctly on submission and reflects current leg states

1.3 on_cancel() Hook

  • Location: transport_job.py line 120-128
  • When: When document is cancelled
  • Action: Sets status to "Cancelled" via db_set() (line 124)

2. Transport Leg Status Changes

2.1 TransportLeg.after_save() Hook

  • Location: transport_leg.py line 39-44
  • When: After Transport Leg is saved (any change to leg)
  • Action: Calls update_transport_job_status() (line 43)

2.2 TransportLeg.update_transport_job_status() Method

  • Location: transport_leg.py line 279-298
  • When: Called from TransportLeg.after_save() hook
  • Actions:
    1. Loads parent Transport Job
    2. Calls job_doc.update_status() to recalculate status
    3. Updates status via db_set() if changed (line 294)
  • 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"

Status Calculation Logic

The update_status() method (line 430-513 in transport_job.py) determines status based on:

For New Documents

  • Sets status to "Draft" if not set

For Cancelled Documents (docstatus = 2)

  • Always sets status to "Cancelled"

For Submitted Documents (docstatus = 1)

  1. Gets all Transport Leg statuses from database
  2. 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)

For Draft Documents (docstatus = 0)

  • Ensures status is "Draft" if missing
  • Doesn't override valid statuses during submission process

Key Implementation Details

Database Updates for Submitted Documents

For submitted documents, status is updated using db_set() instead of save() because:

  • save() would trigger validation which might fail for submitted documents
  • db_set() updates the database directly, bypassing validation
  • This prevents recursive save loops and validation errors

Example from before_save():

if self.docstatus == 1 and old_status != new_status and new_status:
    self.db_set("status", new_status, update_modified=False)

Status Transition Rules

The validate_status_transition() method (line 403-428) 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

Auto-billing Trigger

When status changes to "Completed", _trigger_auto_billing() is called (line 488) to automatically create sales invoices if enabled.

Status Update Flow

Document Creation
    ↓
before_save() → update_status() → Sets "Draft"
    ↓
Document Submit
    ↓
after_submit() → Sets "Submitted" → update_status() → May change to "In Progress" or "Completed"
    ↓
Transport Leg Changes (start_date, end_date, run_sheet, sales_invoice)
    ↓
TransportLeg.after_save() → update_transport_job_status() → update_status() → Updates job status via db_set()
    ↓
Document Save (if status changed)
    ↓
before_save() → update_status() → db_set() if submitted and status changed

Client-Side Updates

JavaScript (transport_job.js)

Form Refresh

  • For submitted documents, fetches latest status from database on form refresh
  • Updates UI to reflect current status
  • No periodic polling - status is fetched only on form load/refresh

Form Submission

  • After submission, fetches latest status with retry mechanism
  • Ensures UI reflects server-side status changes

What Was Removed

Background Jobs (Removed)

  • 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

Periodic Polling (Removed)

  • ❌ 30-second interval status refresh in JavaScript
  • Reason: Status updates happen in real-time via triggers, so polling is not needed

Benefits of Trigger-Based Approach

  1. Real-time Updates: Status updates happen immediately when events occur
  2. Simpler Architecture: No background jobs or polling to maintain
  3. More Reliable: Status is always in sync with actual document/leg states
  4. Better Performance: No unnecessary periodic checks
  5. Easier to Debug: Status changes are directly tied to specific events

Troubleshooting

Status Not Updating

If status is not updating correctly:

  1. Check if document is submitted: Status updates for submitted documents use db_set(), which requires docstatus = 1
  2. Check Transport Leg statuses: Job status is calculated from leg statuses
  3. Check Transport Leg linkage: Ensure legs are properly linked to the Transport Job
  4. Check for errors: Look for errors in logs when hooks are triggered

Manual Status Fix

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" }
})

Summary

The status field is updated through:

  • ✅ Document lifecycle hooks (before_save, after_submit, on_cancel)
  • ✅ Transport Leg status changes (via after_save hook)
  • ✅ Client-side JavaScript (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.

Getting Started

Setup and Settings

Sea Freight

Air Freight

Transport

Customs

Warehousing

Pricing Center

Job Management

Sustainability

Intercompany

Special Projects

Pages

Features

Reports

Glossary

Clone this wiki locally