From f86b51e184d67ac8aa8d6c45ac08ceb1c7a3b8ee Mon Sep 17 00:00:00 2001 From: Emanuel Kagombora Date: Thu, 30 Apr 2026 16:18:37 +0300 Subject: [PATCH 1/3] fix(container interchange): block submit until shipping-line deposit refund & final EIR are recorded (cherry picked from commit fbd904ecaf61f86c1ae5c1d97b3a674cf7289ee2) --- .../doctype/clearing_file/clearing_file.js | 84 +++++++------- .../doctype/clearing_file/clearing_file.py | 22 ++++ .../container_interchange.py | 46 +++++++- .../doctype/tra_clearance/tra_clearance.js | 106 +++++++++++------- 4 files changed, 174 insertions(+), 84 deletions(-) diff --git a/clearing/clearing/doctype/clearing_file/clearing_file.js b/clearing/clearing/doctype/clearing_file/clearing_file.js index eba5c31..dc8d7a0 100644 --- a/clearing/clearing/doctype/clearing_file/clearing_file.js +++ b/clearing/clearing/doctype/clearing_file/clearing_file.js @@ -211,7 +211,7 @@ frappe.ui.form.on("Clearing File", { } if (frm.doc.status === "Pre-Lodged" || frm.doc.status === "On Process") { - // TRA Clearance + // TRA Clearance — always shown at this stage handle_clearance_creation( "TRA Clearance", "TRA Clearance", @@ -225,56 +225,62 @@ frappe.ui.form.on("Clearing File", { "TRA Clearance created successfully" ); - // Shipping Line Clearance (show regardless, server will enforce order) - if (frm.doc.mode_of_transport !== "Air") { + // EWD EX Warehouse Declaration: TRA Clearance is the only required step. + // Skip Shipping Line / Physical Verification / Port Clearance buttons. + const isEWD = frm.doc.cl_plan === "EWD EX Warehouse Declaration"; + + if (!isEWD) { + // Shipping Line Clearance (show regardless, server will enforce order) + if (frm.doc.mode_of_transport !== "Air") { + handle_clearance_creation( + "Shipping Line Clearance", + "Shipping Line Clearance", + { clearing_file: frm.doc.name }, + { + doctype: "Shipping Line Clearance", + clearing_file: frm.doc.name, + customer: frm.doc.customer, + status: "Unpaid", + }, + "Shipping Line Clearance created successfully" + ); + } + + // Physical Verification handle_clearance_creation( - "Shipping Line Clearance", - "Shipping Line Clearance", + "Physical Verification", + "Physical Verification", { clearing_file: frm.doc.name }, { - doctype: "Shipping Line Clearance", + doctype: "Physical Verification", clearing_file: frm.doc.name, customer: frm.doc.customer, - status: "Unpaid", + status: "Payment Pending", }, - "Shipping Line Clearance created successfully" + "Physical Verification created successfully" ); - } - // Physical Verification - handle_clearance_creation( - "Physical Verification", - "Physical Verification", - { clearing_file: frm.doc.name }, - { - doctype: "Physical Verification", + // Port Clearance + let port_clearance_data = { + doctype: "Port Clearance", clearing_file: frm.doc.name, customer: frm.doc.customer, - status: "Payment Pending", - }, - "Physical Verification created successfully" - ); + status: "Unpaid", + }; - // Port Clearance - let port_clearance_data = { - doctype: "Port Clearance", - clearing_file: frm.doc.name, - customer: frm.doc.customer, - status: "Unpaid", - }; - - // Auto-check has_transit_bond for IM8 declaration type - if (frm.doc.declaration_type === "IM8 TRANSIT AND TRANSHIPMENT") { - port_clearance_data.has_transit_bond = 1; - } + // Auto-check has_transit_bond for IM8 declaration type + if (frm.doc.declaration_type === "IM8 TRANSIT AND TRANSHIPMENT") { + port_clearance_data.has_transit_bond = 1; + } - handle_clearance_creation( - "Port Clearance", - "Port Clearance", - { clearing_file: frm.doc.name }, - port_clearance_data, - "Port Clearance created successfully" - ); + handle_clearance_creation( + "Port Clearance", + "Port Clearance", + { clearing_file: frm.doc.name }, + port_clearance_data, + "Port Clearance created successfully" + ); + } } frm.fields_dict.mode_of_transport.df.onchange = () => { diff --git a/clearing/clearing/doctype/clearing_file/clearing_file.py b/clearing/clearing/doctype/clearing_file/clearing_file.py index b1734a8..a6f8d8c 100644 --- a/clearing/clearing/doctype/clearing_file/clearing_file.py +++ b/clearing/clearing/doctype/clearing_file/clearing_file.py @@ -600,6 +600,28 @@ def check_status_change_for_transit_bond(doc, method): def update_status_to_cleared(doc, method): clearing_file_name = doc.clearing_file + # For EWD EX Warehouse Declaration, only TRA Clearance submission + # is required to mark the Clearing File as Cleared. + if doc.doctype == "TRA Clearance": + cl_plan = frappe.db.get_value("Clearing File", clearing_file_name, "cl_plan") + if cl_plan == "EWD EX Warehouse Declaration": + clearing_file_doc = frappe.get_doc("Clearing File", clearing_file_name) + status_now = (clearing_file_doc.status or "").strip() + needs_save = False + + if status_now != "Cleared": + clearing_file_doc.status = "Cleared" + status_now = "Cleared" + needs_save = True + + if status_now == "Cleared" and not clearing_file_doc.cleared_date: + clearing_file_doc.cleared_date = nowdate() + needs_save = True + + if needs_save: + clearing_file_doc.save() + return + # Fetch the Clearing File's mode_of_transport mode_of_transport = frappe.db.get_value( "Clearing File", clearing_file_name, "mode_of_transport" diff --git a/clearing/clearing/doctype/container_interchange/container_interchange.py b/clearing/clearing/doctype/container_interchange/container_interchange.py index 2d73251..ac5601f 100644 --- a/clearing/clearing/doctype/container_interchange/container_interchange.py +++ b/clearing/clearing/doctype/container_interchange/container_interchange.py @@ -1,8 +1,50 @@ # Copyright (c) 2024, Nelson Mpanju and contributors # For license information, please see license.txt -# import frappe +import frappe +from frappe import _ from frappe.model.document import Document +from frappe.utils import flt + class ContainerInterchange(Document): - pass + def before_submit(self): + self.validate_shipping_line_deposit() + + def validate_shipping_line_deposit(self): + """Block submission if no container deposit is recorded in the Shipping Line Clearance, + or if a deposit exists but the refund has not been updated on this Container Interchange.""" + if not self.clearing_file: + return + + deposit_amount = frappe.db.get_value( + "Shipping Line Clearance", + {"clearing_file": self.clearing_file, "docstatus": ["<", 2]}, + "container_deposit_amount", + ) + + if not flt(deposit_amount): + frappe.throw( + _( + "Cannot submit Container Interchange because no Container Deposit Amount " + "is recorded in the Shipping Line Clearance for Clearing File {0}." + ).format(self.clearing_file) + ) + + if not self.refund or not flt(self.refund_amount) or not self.refund_date: + frappe.throw( + _( + "A Container Deposit of {0} is recorded in Shipping Line Clearance for " + "Clearing File {1}. Please update the refund (tick Container Deposit " + "Refunded, set Refund Date and Refund Amount) before submitting." + ).format(flt(deposit_amount), self.clearing_file) + ) + + if not self.final or not self.eir_number_reference or not self.eir_date: + frappe.throw( + _( + "A Container Deposit of {0} is recorded in Shipping Line Clearance for " + "Clearing File {1}. Please update the Final EIR (tick Has Final EIR, " + "set EIR Number Reference and EIR Date) before submitting." + ).format(flt(deposit_amount), self.clearing_file) + ) diff --git a/clearing/clearing/doctype/tra_clearance/tra_clearance.js b/clearing/clearing/doctype/tra_clearance/tra_clearance.js index 39b1ffa..75d6a8c 100644 --- a/clearing/clearing/doctype/tra_clearance/tra_clearance.js +++ b/clearing/clearing/doctype/tra_clearance/tra_clearance.js @@ -14,61 +14,81 @@ frappe.ui.form.on("TRA Clearance", { if (r.message) { const clearing_file_status = r.message.status; const mode_of_transport = r.message.mode_of_transport; + const isEWD = r.message.cl_plan === "EWD EX Warehouse Declaration"; - // Add conditional buttons based on the Clearing File status - if ( - mode_of_transport !== "Air" && - (clearing_file_status === "Pre-Lodged" || - clearing_file_status === "On Process") - ) { + if (isEWD) { + // For EWD EX Warehouse Declaration, after TRA Clearance is + // submitted the next step is CF Delivery Note. No sibling + // clearance buttons are required. + if (frm.doc.docstatus === 1) { + handle_clearance_creation( + "CF Delivery Note", + "CF Delivery Note", + { clearing_file: frm.doc.clearing_file }, + { + doctype: "CF Delivery Note", + clearing_file: frm.doc.clearing_file, + consignee: frm.doc.customer, + }, + "CF Delivery Note created successfully" + ); + } + } else { + // Add conditional buttons based on the Clearing File status + if ( + mode_of_transport !== "Air" && + (clearing_file_status === "Pre-Lodged" || + clearing_file_status === "On Process") + ) { + handle_clearance_creation( + "Shipping Line Clearance", + "Shipping Line Clearance", + { clearing_file: frm.doc.clearing_file }, + { + doctype: "Shipping Line Clearance", + clearing_file: frm.doc.clearing_file, + customer: frm.doc.customer, + status: "Unpaid", + }, + "Shipping Line Clearance created successfully" + ); + } + // Port Clearance button + let port_clearance_data = { + doctype: "Port Clearance", + clearing_file: frm.doc.clearing_file, + customer: frm.doc.customer, + status: "Unpaid", + }; + + // Physical Verification button handle_clearance_creation( - "Shipping Line Clearance", - "Shipping Line Clearance", + "Physical Verification", + "Physical Verification", { clearing_file: frm.doc.clearing_file }, { - doctype: "Shipping Line Clearance", + doctype: "Physical Verification", clearing_file: frm.doc.clearing_file, customer: frm.doc.customer, - status: "Unpaid", + status: "Payment Pending", }, - "Shipping Line Clearance created successfully" + "Physical Verification created successfully" ); - } - // Port Clearance button - let port_clearance_data = { - doctype: "Port Clearance", - clearing_file: frm.doc.clearing_file, - customer: frm.doc.customer, - status: "Unpaid", - }; - // Physical Verification button - handle_clearance_creation( - "Physical Verification", - "Physical Verification", - { clearing_file: frm.doc.clearing_file }, - { - doctype: "Physical Verification", - clearing_file: frm.doc.clearing_file, - customer: frm.doc.customer, - status: "Payment Pending", - }, - "Physical Verification created successfully" - ); + // Auto-check has_transit_bond for IM8 declaration type + if (r.message.declaration_type === "IM8 TRANSIT AND TRANSHIPMENT") { + port_clearance_data.has_transit_bond = 1; + } - // Auto-check has_transit_bond for IM8 declaration type - if (r.message.declaration_type === "IM8 TRANSIT AND TRANSHIPMENT") { - port_clearance_data.has_transit_bond = 1; + handle_clearance_creation( + "Port Clearance", + "Port Clearance", + { clearing_file: frm.doc.clearing_file }, + port_clearance_data, + "Port Clearance created successfully" + ); } - handle_clearance_creation( - "Port Clearance", - "Port Clearance", - { clearing_file: frm.doc.clearing_file }, - port_clearance_data, - "Port Clearance created successfully" - ); - // Refresh buttons display frm.refresh_fields(); } From 89c315613ccf0aaa9e108c45436b610c73905899 Mon Sep 17 00:00:00 2001 From: Emanuel Kagombora Date: Wed, 6 May 2026 13:13:48 +0300 Subject: [PATCH 2/3] fix(container interchange): skip deposit/refund/EIR checks when no Shipping Line Clearance exists (cherry picked from commit 21a54d9312f804134c113cb8399aa045fd96ba79) --- .../container_interchange/container_interchange.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/clearing/clearing/doctype/container_interchange/container_interchange.py b/clearing/clearing/doctype/container_interchange/container_interchange.py index ac5601f..efe03df 100644 --- a/clearing/clearing/doctype/container_interchange/container_interchange.py +++ b/clearing/clearing/doctype/container_interchange/container_interchange.py @@ -12,8 +12,6 @@ def before_submit(self): self.validate_shipping_line_deposit() def validate_shipping_line_deposit(self): - """Block submission if no container deposit is recorded in the Shipping Line Clearance, - or if a deposit exists but the refund has not been updated on this Container Interchange.""" if not self.clearing_file: return @@ -24,12 +22,7 @@ def validate_shipping_line_deposit(self): ) if not flt(deposit_amount): - frappe.throw( - _( - "Cannot submit Container Interchange because no Container Deposit Amount " - "is recorded in the Shipping Line Clearance for Clearing File {0}." - ).format(self.clearing_file) - ) + return if not self.refund or not flt(self.refund_amount) or not self.refund_date: frappe.throw( From 5ab309b2769ebe612993b5eab194a71e855d251f Mon Sep 17 00:00:00 2001 From: Emanuel Kagombora Date: Wed, 6 May 2026 14:44:12 +0300 Subject: [PATCH 3/3] feat(container interchange): require Final EIR when CF Delivery Note is linked (cherry picked from commit 24d914f1ea9488dcdfcc722644b7e8f6936ff813) --- .../container_interchange.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/clearing/clearing/doctype/container_interchange/container_interchange.py b/clearing/clearing/doctype/container_interchange/container_interchange.py index efe03df..7850750 100644 --- a/clearing/clearing/doctype/container_interchange/container_interchange.py +++ b/clearing/clearing/doctype/container_interchange/container_interchange.py @@ -10,6 +10,25 @@ class ContainerInterchange(Document): def before_submit(self): self.validate_shipping_line_deposit() + self.validate_final_eir_against_delivery_note() + + def validate_final_eir_against_delivery_note(self): + if not self.clearing_file: + return + + has_linked_delivery_note = bool( + frappe.db.exists( + "CF Delivery Note", + {"container_interchange": self.name, "docstatus": ["<", 2]}, + ) + ) + if not has_linked_delivery_note: + return + + if not self.final or not self.eir_number_reference or not self.eir_date: + frappe.throw( + _("Final EIR is required (Has Final EIR, EIR Number Reference, EIR Date) before submitting.") + ) def validate_shipping_line_deposit(self): if not self.clearing_file: