From 611b8dbd7c8313e83404f8d4f9e931c9a434696e Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:02 +0300 Subject: [PATCH 01/13] fix: drop the client script for the removed Bank Reconciliation --- csf_tz/hooks.py | 1 - 1 file changed, 1 deletion(-) diff --git a/csf_tz/hooks.py b/csf_tz/hooks.py index 95d2a6e8..5ecfb2c2 100755 --- a/csf_tz/hooks.py +++ b/csf_tz/hooks.py @@ -54,7 +54,6 @@ "Quotation": "csf_tz/quotation.js", "Purchase Receipt": "csf_tz/purchase_receipt.js", "Purchase Order": "csf_tz/purchase_order.js", - "Bank Reconciliation": "csf_tz/bank_reconciliation.js", "Payroll Entry": [ "csf_tz/payroll_entry.js", "stanbic/payroll_entry.js", From 946ea3e5f9616d224a23f66cd22974f8cd6584e3 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:07 +0300 Subject: [PATCH 02/13] chore: remove the Bank Reconciliation client script --- csf_tz/csf_tz/bank_reconciliation.js | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 csf_tz/csf_tz/bank_reconciliation.js diff --git a/csf_tz/csf_tz/bank_reconciliation.js b/csf_tz/csf_tz/bank_reconciliation.js deleted file mode 100644 index fec5229c..00000000 --- a/csf_tz/csf_tz/bank_reconciliation.js +++ /dev/null @@ -1,21 +0,0 @@ -frappe.ui.form.on('Bank Reconciliation', { - get_payment_entries: function (frm) { - frappe.call({ - method: 'erpnext.accounts.utils.get_balance_on', - args: { - account: frm.doc.account, - date: frappe.datetime.add_days(frm.doc.from_date, -1), - }, - async: false, - callback: function (r) { - if (r.message) { - frm.set_value("opening_balance", r.message || 0); - } - else { - frm.set_value("opening_balance", 0); - } - } - }); - frm.set_value("closing_balance", frm.doc.total_amount + frm.doc.opening_balance); - }, -}); From 03a5a2fbf066f4f17d5bbd93cb91b96c32b5e55a Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:08 +0300 Subject: [PATCH 03/13] fix: require a company before loading warehouse options --- csf_tz/csf_tz/stock_entry.js | 1 + 1 file changed, 1 insertion(+) diff --git a/csf_tz/csf_tz/stock_entry.js b/csf_tz/csf_tz/stock_entry.js index a3402a86..66bb1f4a 100644 --- a/csf_tz/csf_tz/stock_entry.js +++ b/csf_tz/csf_tz/stock_entry.js @@ -61,6 +61,7 @@ frappe.ui.form.on("Stock Entry", { refresh_field("total_net_weight"); }, set_warehouse_options: function (frm) { + if (!frm.doc.company) return; frappe.call({ "method": "csf_tz.custom_api.get_warehouse_options", "args": { company: frm.doc.company }, From 9e952d2ad044af042721989ae916e985c5889121 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:08 +0300 Subject: [PATCH 04/13] fix: load CSF TZ settings before reading the UOM flag --- csf_tz/csf_tz/sales_order.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/csf_tz/csf_tz/sales_order.js b/csf_tz/csf_tz/sales_order.js index bf3ec9db..752c9609 100644 --- a/csf_tz/csf_tz/sales_order.js +++ b/csf_tz/csf_tz/sales_order.js @@ -3,12 +3,10 @@ frappe.require([ '/assets/csf_tz/js/shortcuts.js' ]); -frappe.ui.form.on("Sales Order", { - // preload settings as a Promise - onload: function (frm) { +function csf_settings(frm) { + if (!frm._csf_settings_promise) { frm._csf_settings_promise = (async () => { try { - // Fetch both fields in one go const limit = await frappe.db.get_single_value( "CSF TZ Settings", "limit_uom_as_item_uom" @@ -27,9 +25,16 @@ frappe.ui.form.on("Sales Order", { return {}; } })(); + } + return frm._csf_settings_promise; +} + +frappe.ui.form.on("Sales Order", { + onload: function (frm) { + csf_settings(frm); }, refresh: async function (frm) { - const settings = await frm._csf_settings_promise; + const settings = await csf_settings(frm); if (settings.limit_uom_as_item_uom === 1) { frm.set_query("uom", "items", function (frm, cdt, cdn) { let row = locals[cdt][cdn]; @@ -45,7 +50,7 @@ frappe.ui.form.on("Sales Order", { }, customer: async function (frm) { if (!frm.doc.customer) return; - const settings = await frm._csf_settings_promise; + const settings = await csf_settings(frm); if (settings.show_customer_outstanding_in_sales_order === 1) { frappe.call({ method: 'csf_tz.csftz_hooks.customer.get_customer_total_unpaid_amount', From 15c4b8dd73361cf406d608fb06ec05f2adf22fa8 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:09 +0300 Subject: [PATCH 05/13] fix: query BOM additional costs only when the table exists --- csf_tz/csf_tz/bom_addittional_costs.js | 1 + 1 file changed, 1 insertion(+) diff --git a/csf_tz/csf_tz/bom_addittional_costs.js b/csf_tz/csf_tz/bom_addittional_costs.js index 60b1007e..2749486c 100644 --- a/csf_tz/csf_tz/bom_addittional_costs.js +++ b/csf_tz/csf_tz/bom_addittional_costs.js @@ -1,5 +1,6 @@ frappe.ui.form.on("BOM", { refresh: function (frm) { + if (!frm.fields_dict.additional_costs) return; frm.set_query("expense_account", "additional_costs", function () { return { filters: { From b5504643d3c6cab5db79d84892b46e77c16820eb Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:10 +0300 Subject: [PATCH 06/13] fix: link journal entries to /desk and wait for the company --- csf_tz/csf_tz/payment_entry.js | 739 +++++++++++++++++---------------- 1 file changed, 372 insertions(+), 367 deletions(-) diff --git a/csf_tz/csf_tz/payment_entry.js b/csf_tz/csf_tz/payment_entry.js index fe180134..63a759b8 100644 --- a/csf_tz/csf_tz/payment_entry.js +++ b/csf_tz/csf_tz/payment_entry.js @@ -1,373 +1,378 @@ -frappe.ui.form.on("Payment Entry", { - onload: function (frm) { - if (frm.is_new()) { - frm.trigger("payment_type"); - } - }, - refresh: function (frm) { - frm.trigger("add_write_off_button"); - }, - payment_type: function (frm) { - if (frm.is_new()) { - if (frm.doc.payment_type == "Receive") { - frm.set_value("naming_series", "RE-.YYYY.-"); - if (!["Student", "Donor"].includes(frm.doc.party_type)) { - frm.set_value("party_type", "Customer"); - } - } - else if (frm.doc.payment_type == "Pay") { - frm.set_value("naming_series", "PE-.YYYY.-"); - if (frm.doc.party_type != "Employee") { - frm.set_value("party_type", "Supplier"); - } - } - else if (frm.doc.payment_type == "Internal Transfer") { - frm.set_value("naming_series", "IT-.YYYY.-"); - frm.set_value("party_type", ""); - frm.set_value("party_name", ""); - } - } - frm.refresh_fields() - }, - - party: function (frm) { - if (frm.is_new()) { - // check if the feature is disabled in CSF TZ Settings - frappe.db.get_single_value("CSF TZ Settings", "disable_get_outstanding_functionality") - .then(disabled => { - if (disabled) { - // Feature is disabled, do not proceed with get_outstanding_documents - return; - } - - // Feature is enabled, proceed with existing functionality - const today = frappe.datetime.get_today(); - const filters = { - from_posting_date: frappe.datetime.add_days(today, -3650), - to_posting_date: today, - allocate_payment_amount: 1 - } - if (["Customer", "Supplier"].includes(frm.doc.party_type) && frm.doc.paid_from_account_currency && frm.doc.paid_to_account_currency) { - frm.events.get_outstanding_documents(frm, filters); - } - }); - } - }, - - get_outstanding_documents: function (frm, filters) { - // first check if the feature s disabled in CSF TZ Settings - return frappe.db.get_single_value("CSF TZ Settings", "disable_get_outstanding_functionality") - .then(disabled => { - if (disabled) { - // Feature is disabled, do not proceed - return; - } - - // Continue with normal functionality - if (typeof frappe.route_history[frappe.route_history.length - 2] != "undefined") { - if (frappe.route_history[frappe.route_history.length - 2][1] in ["Sales Invoice", "Employee Advance", "Purchase Invoice"]) { - return; - } - } - - frm.clear_table("references"); - - if (!frm.doc.party) { - return; - } - - frm.events.check_mandatory_to_fetch(frm); - - // Ensure party account is set based on payment type - var party_account = frm.doc.payment_type == "Receive" ? frm.doc.paid_from : frm.doc.paid_to; - if (!party_account) { - frappe.msgprint(__("Please set the appropriate account for the selected payment type.")); - return; - } - - var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; - - var args = { - "posting_date": frm.doc.posting_date, - "company": frm.doc.company, - "party_type": frm.doc.party_type, - "payment_type": frm.doc.payment_type, - "party": frm.doc.party, - "party_account": party_account, - "cost_center": frm.doc.cost_center - } - - for (let key in filters) { - args[key] = filters[key]; - } - - frappe.flags.allocate_payment_amount = filters['allocate_payment_amount']; - - - return frappe.call({ - method: 'csf_tz.csftz_hooks.payment_entry.get_outstanding_reference_documents', - args: { - args: args - }, - callback: function (r, rt) { - if (r.message) { - var total_positive_outstanding = 0; - var total_negative_outstanding = 0; - - $.each(r.message, function (i, d) { - var c = frm.add_child("references"); - c.reference_doctype = d.voucher_type; - c.reference_name = d.voucher_no; - c.due_date = d.due_date; - c.posting_date = d.posting_date; - c.total_amount = d.invoice_amount; - c.outstanding_amount = d.outstanding_amount; - c.bill_no = d.bill_no; - - if (!in_list(["Sales Order", "Purchase Order", "Expense Claim", "Fees"], d.voucher_type)) { - if (flt(d.outstanding_amount) > 0) - total_positive_outstanding += flt(d.outstanding_amount); - else - total_negative_outstanding += Math.abs(flt(d.outstanding_amount)); - } - - var party_account_currency = frm.doc.payment_type == "Receive" ? - frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency; - - if (party_account_currency != company_currency) { - c.exchange_rate = d.exchange_rate; - } else { - c.exchange_rate = 1; - } - if (in_list(['Sales Invoice', 'Purchase Invoice', "Expense Claim", "Fees"], d.reference_doctype)) { - c.due_date = d.due_date; - } - }); - - if ( - (frm.doc.payment_type == "Receive" && frm.doc.party_type == "Customer") || - (frm.doc.payment_type == "Pay" && frm.doc.party_type == "Supplier") || - (frm.doc.payment_type == "Pay" && frm.doc.party_type == "Employee") || - (frm.doc.payment_type == "Receive" && frm.doc.party_type == "Student") - ) { - if (total_positive_outstanding > total_negative_outstanding) - if (!frm.doc.paid_amount) - frm.set_value("paid_amount", - total_positive_outstanding - total_negative_outstanding); - } else if ( - total_negative_outstanding && - total_positive_outstanding < total_negative_outstanding - ) { - if (!frm.doc.received_amount) - frm.set_value("received_amount", - total_negative_outstanding - total_positive_outstanding); - } - } - - const paid_amount = frm.doc.payment_type == "Receive" ? frm.doc.paid_amount : frm.doc.received_amount; - if (paid_amount) { - frm.events.allocate_party_amount_against_ref_docs(frm, paid_amount, true); - } - - } - }); - }); - }, - get_outstanding_so: function (frm) { - const today = frappe.datetime.get_today(); - let fields = [ - { fieldtype: "Section Break", label: __("Posting Date") }, - { - fieldtype: "Date", - label: __("From Date"), - fieldname: "from_posting_date", - default: frappe.datetime.add_days(today, -30), - }, - { fieldtype: "Column Break" }, - { fieldtype: "Date", label: __("To Date"), fieldname: "to_posting_date", default: today }, - { fieldtype: "Section Break", label: __("Due Date") }, - { fieldtype: "Date", label: __("From Date"), fieldname: "from_due_date" }, - { fieldtype: "Column Break" }, - { fieldtype: "Date", label: __("To Date"), fieldname: "to_due_date" }, - { fieldtype: "Section Break", label: __("Outstanding Amount") }, - { - fieldtype: "Float", - label: __("Greater Than Amount"), - fieldname: "outstanding_amt_greater_than", - default: 0, - }, - { fieldtype: "Column Break" }, - { fieldtype: "Float", label: __("Less Than Amount"), fieldname: "outstanding_amt_less_than" }, - { - fieldtype: "Check", - label: __("Allocate Payment Amount"), - fieldname: "allocate_payment_amount", - default: 1 - } - ]; - - frappe.prompt( - fields, - function (filters) { - frm.clear_table("references"); - - if (!frm.doc.party) { - frappe.throw(__("Please select a Party first")); - return; - } - - frm.events.check_mandatory_to_fetch(frm); - - // Ensure party account is set based on payment type - var party_account = frm.doc.payment_type == "Receive" ? frm.doc.paid_from : frm.doc.paid_to; - if (!party_account) { - frappe.msgprint(__("Please set the appropriate account for the selected payment type.")); - return; - } - - frappe.flags.allocate_payment_amount = filters.allocate_payment_amount; - - var args = { - "posting_date": frm.doc.posting_date, - "company": frm.doc.company, - "party_type": frm.doc.party_type, - "payment_type": frm.doc.payment_type, - "party": frm.doc.party, - "party_account": party_account, - "cost_center": frm.doc.cost_center, - "from_posting_date": filters.from_posting_date, - "to_posting_date": filters.to_posting_date, - "from_due_date": filters.from_due_date, - "to_due_date": filters.to_due_date, - "outstanding_amt_greater_than": filters.outstanding_amt_greater_than, - "outstanding_amt_less_than": filters.outstanding_amt_less_than, - "allocate_payment_amount": filters.allocate_payment_amount - }; - - return frappe.call({ - method: 'csf_tz.csftz_hooks.payment_entry.get_outstanding_sales_orders', - args: { - args: args - }, - callback: function (r, rt) { - if (r.message) { - var total_positive_outstanding = 0; - - $.each(r.message, function (i, d) { - var c = frm.add_child("references"); - c.reference_doctype = d.voucher_type; - c.reference_name = d.voucher_no; - c.due_date = d.due_date; - c.posting_date = d.posting_date; - c.total_amount = d.invoice_amount; - c.outstanding_amount = d.outstanding_amount; - - // Add to total outstanding - total_positive_outstanding += flt(d.outstanding_amount); - - var party_account_currency = frm.doc.payment_type == "Receive" ? - frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency; - - var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; - - if (party_account_currency != company_currency) { - c.exchange_rate = d.exchange_rate; - } else { - c.exchange_rate = 1; - } - }); - - // Set paid amount based on outstanding sales orders - if (total_positive_outstanding > 0 && frappe.flags.allocate_payment_amount) { - if (frm.doc.payment_type == "Receive" && !frm.doc.paid_amount) { - frm.set_value("paid_amount", total_positive_outstanding); - } else if (frm.doc.payment_type == "Pay" && !frm.doc.received_amount) { - frm.set_value("received_amount", total_positive_outstanding); - } - } - - frm.refresh_fields(); - - const paid_amount = frm.doc.payment_type == "Receive" ? frm.doc.paid_amount : frm.doc.received_amount; - if (paid_amount && frappe.flags.allocate_payment_amount) { - frm.events.allocate_party_amount_against_ref_docs(frm, paid_amount, true); - } - } - } - }); - }, - __("Filters"), - __("Get Sales Orders") - ); - }, - - // Write-off Journal Entry Feature - add_write_off_button: function (frm) { - // Check if feature is enabled and conditions are met - frappe.db - .get_single_value("CSF TZ Settings", "enable_write_off_jv_pe") - .then((enable_write_off) => { - if (enable_write_off && - frm.doc.docstatus === 1 && - frm.doc.unallocated_amount > 0) { - - frm.add_custom_button(__("Write Off Outstanding"), function () { - // Fetch the write-off account from Company before showing the dialog - frappe.db.get_value("Company", frm.doc.company, "write_off_account").then(function(r) { - let write_off_account = r.message ? r.message.write_off_account : null; - - // Show dialog to select write-off account - let dialog = new frappe.ui.Dialog({ - title: __("Write Off Unallocated Amount"), - fields: [ - { - fieldname: "write_off_account", - label: __("Write Off Account"), - fieldtype: "Link", - options: "Account", - "default": write_off_account, - reqd: 1, - get_query: function() { - return { - filters: { - "report_type": "Balance Sheet", - "is_group": 0, - "company": frm.doc.company - } - }; - } - }, - { - fieldname: "unallocated_amount", - label: __("Unallocated Amount"), - fieldtype: "Currency", - default: frm.doc.unallocated_amount, - read_only: 1 - } - ], - primary_action_label: __("Create Write Off Entry"), - primary_action: function(values) { - frappe.call({ - method: "csf_tz.custom_api.create_write_off_jv_pe", - args: { - payment_entry: frm.doc.name, - account: values.write_off_account - }, +frappe.ui.form.on("Payment Entry", { + onload: function (frm) { + if (frm.is_new() && frm.doc.company) { + frm.trigger("payment_type"); + } + }, + company: function (frm) { + if (frm.is_new() && frm.doc.company) { + frm.trigger("payment_type"); + } + }, + refresh: function (frm) { + frm.trigger("add_write_off_button"); + }, + payment_type: function (frm) { + if (frm.is_new()) { + if (frm.doc.payment_type == "Receive") { + frm.set_value("naming_series", "RE-.YYYY.-"); + if (!["Student", "Donor"].includes(frm.doc.party_type)) { + frm.set_value("party_type", "Customer"); + } + } + else if (frm.doc.payment_type == "Pay") { + frm.set_value("naming_series", "PE-.YYYY.-"); + if (frm.doc.party_type != "Employee") { + frm.set_value("party_type", "Supplier"); + } + } + else if (frm.doc.payment_type == "Internal Transfer") { + frm.set_value("naming_series", "IT-.YYYY.-"); + frm.set_value("party_type", ""); + frm.set_value("party_name", ""); + } + } + frm.refresh_fields() + }, + + party: function (frm) { + if (frm.is_new()) { + // check if the feature is disabled in CSF TZ Settings + frappe.db.get_single_value("CSF TZ Settings", "disable_get_outstanding_functionality") + .then(disabled => { + if (disabled) { + // Feature is disabled, do not proceed with get_outstanding_documents + return; + } + + // Feature is enabled, proceed with existing functionality + const today = frappe.datetime.get_today(); + const filters = { + from_posting_date: frappe.datetime.add_days(today, -3650), + to_posting_date: today, + allocate_payment_amount: 1 + } + if (["Customer", "Supplier"].includes(frm.doc.party_type) && frm.doc.paid_from_account_currency && frm.doc.paid_to_account_currency) { + frm.events.get_outstanding_documents(frm, filters); + } + }); + } + }, + + get_outstanding_documents: function (frm, filters) { + // first check if the feature s disabled in CSF TZ Settings + return frappe.db.get_single_value("CSF TZ Settings", "disable_get_outstanding_functionality") + .then(disabled => { + if (disabled) { + // Feature is disabled, do not proceed + return; + } + + // Continue with normal functionality + if (typeof frappe.route_history[frappe.route_history.length - 2] != "undefined") { + if (frappe.route_history[frappe.route_history.length - 2][1] in ["Sales Invoice", "Employee Advance", "Purchase Invoice"]) { + return; + } + } + + frm.clear_table("references"); + + if (!frm.doc.party) { + return; + } + + frm.events.check_mandatory_to_fetch(frm); + + // Ensure party account is set based on payment type + var party_account = frm.doc.payment_type == "Receive" ? frm.doc.paid_from : frm.doc.paid_to; + if (!party_account) { + frappe.msgprint(__("Please set the appropriate account for the selected payment type.")); + return; + } + + var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; + + var args = { + "posting_date": frm.doc.posting_date, + "company": frm.doc.company, + "party_type": frm.doc.party_type, + "payment_type": frm.doc.payment_type, + "party": frm.doc.party, + "party_account": party_account, + "cost_center": frm.doc.cost_center + } + + for (let key in filters) { + args[key] = filters[key]; + } + + frappe.flags.allocate_payment_amount = filters['allocate_payment_amount']; + + + return frappe.call({ + method: 'csf_tz.csftz_hooks.payment_entry.get_outstanding_reference_documents', + args: { + args: args + }, + callback: function (r, rt) { + if (r.message) { + var total_positive_outstanding = 0; + var total_negative_outstanding = 0; + + $.each(r.message, function (i, d) { + var c = frm.add_child("references"); + c.reference_doctype = d.voucher_type; + c.reference_name = d.voucher_no; + c.due_date = d.due_date; + c.posting_date = d.posting_date; + c.total_amount = d.invoice_amount; + c.outstanding_amount = d.outstanding_amount; + c.bill_no = d.bill_no; + + if (!in_list(["Sales Order", "Purchase Order", "Expense Claim", "Fees"], d.voucher_type)) { + if (flt(d.outstanding_amount) > 0) + total_positive_outstanding += flt(d.outstanding_amount); + else + total_negative_outstanding += Math.abs(flt(d.outstanding_amount)); + } + + var party_account_currency = frm.doc.payment_type == "Receive" ? + frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency; + + if (party_account_currency != company_currency) { + c.exchange_rate = d.exchange_rate; + } else { + c.exchange_rate = 1; + } + if (in_list(['Sales Invoice', 'Purchase Invoice', "Expense Claim", "Fees"], d.reference_doctype)) { + c.due_date = d.due_date; + } + }); + + if ( + (frm.doc.payment_type == "Receive" && frm.doc.party_type == "Customer") || + (frm.doc.payment_type == "Pay" && frm.doc.party_type == "Supplier") || + (frm.doc.payment_type == "Pay" && frm.doc.party_type == "Employee") || + (frm.doc.payment_type == "Receive" && frm.doc.party_type == "Student") + ) { + if (total_positive_outstanding > total_negative_outstanding) + if (!frm.doc.paid_amount) + frm.set_value("paid_amount", + total_positive_outstanding - total_negative_outstanding); + } else if ( + total_negative_outstanding && + total_positive_outstanding < total_negative_outstanding + ) { + if (!frm.doc.received_amount) + frm.set_value("received_amount", + total_negative_outstanding - total_positive_outstanding); + } + } + + const paid_amount = frm.doc.payment_type == "Receive" ? frm.doc.paid_amount : frm.doc.received_amount; + if (paid_amount) { + frm.events.allocate_party_amount_against_ref_docs(frm, paid_amount, true); + } + + } + }); + }); + }, + get_outstanding_so: function (frm) { + const today = frappe.datetime.get_today(); + let fields = [ + { fieldtype: "Section Break", label: __("Posting Date") }, + { + fieldtype: "Date", + label: __("From Date"), + fieldname: "from_posting_date", + default: frappe.datetime.add_days(today, -30), + }, + { fieldtype: "Column Break" }, + { fieldtype: "Date", label: __("To Date"), fieldname: "to_posting_date", default: today }, + { fieldtype: "Section Break", label: __("Due Date") }, + { fieldtype: "Date", label: __("From Date"), fieldname: "from_due_date" }, + { fieldtype: "Column Break" }, + { fieldtype: "Date", label: __("To Date"), fieldname: "to_due_date" }, + { fieldtype: "Section Break", label: __("Outstanding Amount") }, + { + fieldtype: "Float", + label: __("Greater Than Amount"), + fieldname: "outstanding_amt_greater_than", + default: 0, + }, + { fieldtype: "Column Break" }, + { fieldtype: "Float", label: __("Less Than Amount"), fieldname: "outstanding_amt_less_than" }, + { + fieldtype: "Check", + label: __("Allocate Payment Amount"), + fieldname: "allocate_payment_amount", + default: 1 + } + ]; + + frappe.prompt( + fields, + function (filters) { + frm.clear_table("references"); + + if (!frm.doc.party) { + frappe.throw(__("Please select a Party first")); + return; + } + + frm.events.check_mandatory_to_fetch(frm); + + // Ensure party account is set based on payment type + var party_account = frm.doc.payment_type == "Receive" ? frm.doc.paid_from : frm.doc.paid_to; + if (!party_account) { + frappe.msgprint(__("Please set the appropriate account for the selected payment type.")); + return; + } + + frappe.flags.allocate_payment_amount = filters.allocate_payment_amount; + + var args = { + "posting_date": frm.doc.posting_date, + "company": frm.doc.company, + "party_type": frm.doc.party_type, + "payment_type": frm.doc.payment_type, + "party": frm.doc.party, + "party_account": party_account, + "cost_center": frm.doc.cost_center, + "from_posting_date": filters.from_posting_date, + "to_posting_date": filters.to_posting_date, + "from_due_date": filters.from_due_date, + "to_due_date": filters.to_due_date, + "outstanding_amt_greater_than": filters.outstanding_amt_greater_than, + "outstanding_amt_less_than": filters.outstanding_amt_less_than, + "allocate_payment_amount": filters.allocate_payment_amount + }; + + return frappe.call({ + method: 'csf_tz.csftz_hooks.payment_entry.get_outstanding_sales_orders', + args: { + args: args + }, + callback: function (r, rt) { + if (r.message) { + var total_positive_outstanding = 0; + + $.each(r.message, function (i, d) { + var c = frm.add_child("references"); + c.reference_doctype = d.voucher_type; + c.reference_name = d.voucher_no; + c.due_date = d.due_date; + c.posting_date = d.posting_date; + c.total_amount = d.invoice_amount; + c.outstanding_amount = d.outstanding_amount; + + // Add to total outstanding + total_positive_outstanding += flt(d.outstanding_amount); + + var party_account_currency = frm.doc.payment_type == "Receive" ? + frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency; + + var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; + + if (party_account_currency != company_currency) { + c.exchange_rate = d.exchange_rate; + } else { + c.exchange_rate = 1; + } + }); + + // Set paid amount based on outstanding sales orders + if (total_positive_outstanding > 0 && frappe.flags.allocate_payment_amount) { + if (frm.doc.payment_type == "Receive" && !frm.doc.paid_amount) { + frm.set_value("paid_amount", total_positive_outstanding); + } else if (frm.doc.payment_type == "Pay" && !frm.doc.received_amount) { + frm.set_value("received_amount", total_positive_outstanding); + } + } + + frm.refresh_fields(); + + const paid_amount = frm.doc.payment_type == "Receive" ? frm.doc.paid_amount : frm.doc.received_amount; + if (paid_amount && frappe.flags.allocate_payment_amount) { + frm.events.allocate_party_amount_against_ref_docs(frm, paid_amount, true); + } + } + } + }); + }, + __("Filters"), + __("Get Sales Orders") + ); + }, + + // Write-off Journal Entry Feature + add_write_off_button: function (frm) { + // Check if feature is enabled and conditions are met + frappe.db + .get_single_value("CSF TZ Settings", "enable_write_off_jv_pe") + .then((enable_write_off) => { + if (enable_write_off && + frm.doc.docstatus === 1 && + frm.doc.unallocated_amount > 0) { + + frm.add_custom_button(__("Write Off Outstanding"), function () { + // Fetch the write-off account from Company before showing the dialog + frappe.db.get_value("Company", frm.doc.company, "write_off_account").then(function(r) { + let write_off_account = r.message ? r.message.write_off_account : null; + + // Show dialog to select write-off account + let dialog = new frappe.ui.Dialog({ + title: __("Write Off Unallocated Amount"), + fields: [ + { + fieldname: "write_off_account", + label: __("Write Off Account"), + fieldtype: "Link", + options: "Account", + "default": write_off_account, + reqd: 1, + get_query: function() { + return { + filters: { + "report_type": "Balance Sheet", + "is_group": 0, + "company": frm.doc.company + } + }; + } + }, + { + fieldname: "unallocated_amount", + label: __("Unallocated Amount"), + fieldtype: "Currency", + default: frm.doc.unallocated_amount, + read_only: 1 + } + ], + primary_action_label: __("Create Write Off Entry"), + primary_action: function(values) { + frappe.call({ + method: "csf_tz.custom_api.create_write_off_jv_pe", + args: { + payment_entry: frm.doc.name, + account: values.write_off_account + }, callback: function(r) { if (r.message) { - const journal_entry_link = `${frappe.utils.escape_html(r.message)}`; + const journal_entry_link = `${frappe.utils.escape_html(r.message)}`; frappe.msgprint(__("Write-off Journal Entry created: {0}", [journal_entry_link])); frm.reload_doc(); } } - }); - dialog.hide(); - } - }); - dialog.show(); - }); - }, __("Create")); - } - }); - } -}); + }); + dialog.hide(); + } + }); + dialog.show(); + }); + }, __("Create")); + } + }); + } +}); From 8b3b7ca9cea6c982f0a6fa46dc3dc54245c92f65 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:11 +0300 Subject: [PATCH 07/13] fix: open the payroll report on the v16 desk route --- csf_tz/csf_tz/payroll_entry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csf_tz/csf_tz/payroll_entry.js b/csf_tz/csf_tz/payroll_entry.js index 7b0949ad..c0ce35cc 100644 --- a/csf_tz/csf_tz/payroll_entry.js +++ b/csf_tz/csf_tz/payroll_entry.js @@ -11,7 +11,7 @@ frappe.ui.form.on("Payroll Entry", { // Redirect with filter const report_name = "Salary Register"; - let report_url = `/app/query-report/${encodeURIComponent(report_name)}?from_date=${encodeURIComponent(frm.doc.start_date)}&to_date=${encodeURIComponent(frm.doc.end_date)}${frm.doc.company ? `&company=${encodeURIComponent(frm.doc.company)}` : ""}&payroll_entry=${encodeURIComponent(frm.doc.name)}`; + let report_url = `/desk/query-report/${encodeURIComponent(report_name)}?from_date=${encodeURIComponent(frm.doc.start_date)}&to_date=${encodeURIComponent(frm.doc.end_date)}${frm.doc.company ? `&company=${encodeURIComponent(frm.doc.company)}` : ""}&payroll_entry=${encodeURIComponent(frm.doc.name)}`; window.open(report_url, "_blank"); }).addClass('btn-primary'); From b18708561fe63382aa296069d3ae0f78485bbec3 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:12 +0300 Subject: [PATCH 08/13] fix: link the write off journal entry to the desk route --- csf_tz/csf_tz/sales_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csf_tz/csf_tz/sales_invoice.js b/csf_tz/csf_tz/sales_invoice.js index 2abd08e1..e4b7e89a 100644 --- a/csf_tz/csf_tz/sales_invoice.js +++ b/csf_tz/csf_tz/sales_invoice.js @@ -189,7 +189,7 @@ frappe.ui.form.on("Sales Invoice", { }, callback: function(r) { if (r.message) { - const journal_entry_link = `${frappe.utils.escape_html(r.message)}`; + const journal_entry_link = `${frappe.utils.escape_html(r.message)}`; frappe.msgprint(__("Write-off Journal Entry created: {0}", [journal_entry_link])); frm.reload_doc(); } From 679f66fd15cc40a9b04b85d4efe9ea2ccccdbb1c Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:13 +0300 Subject: [PATCH 09/13] fix: link the write off journal entry to the desk route --- csf_tz/csf_tz/purchase_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csf_tz/csf_tz/purchase_invoice.js b/csf_tz/csf_tz/purchase_invoice.js index 758f095f..b7caf582 100644 --- a/csf_tz/csf_tz/purchase_invoice.js +++ b/csf_tz/csf_tz/purchase_invoice.js @@ -135,7 +135,7 @@ frappe.ui.form.on("Purchase Invoice", { }, callback: function(r) { if (r.message) { - const journal_entry_link = `${frappe.utils.escape_html(r.message)}`; + const journal_entry_link = `${frappe.utils.escape_html(r.message)}`; frappe.msgprint(__("Write-off Journal Entry created: {0}", [journal_entry_link])); frm.reload_doc(); } From f66b0e14c87457de53753b13e2d982342e73a735 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:13 +0300 Subject: [PATCH 10/13] fix: render the QR scanner as a page fragment --- .../csf_tz/page/scan_qrcode/scan_qrcode.html | 116 +++++------------- 1 file changed, 32 insertions(+), 84 deletions(-) diff --git a/csf_tz/csf_tz/page/scan_qrcode/scan_qrcode.html b/csf_tz/csf_tz/page/scan_qrcode/scan_qrcode.html index 9f3013d0..9fb9321d 100644 --- a/csf_tz/csf_tz/page/scan_qrcode/scan_qrcode.html +++ b/csf_tz/csf_tz/page/scan_qrcode/scan_qrcode.html @@ -1,84 +1,32 @@ - - - - - - - QR Code Reader - - - -
-
-
-
- - - - + +
+
+
From b6119589b79784db8fc0bc1e3e784baa2c517056 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:14 +0300 Subject: [PATCH 11/13] fix: load the QR scanner library before starting it --- csf_tz/csf_tz/page/scan_qrcode/scan_qrcode.js | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/csf_tz/csf_tz/page/scan_qrcode/scan_qrcode.js b/csf_tz/csf_tz/page/scan_qrcode/scan_qrcode.js index 6ccfae99..bde376ed 100644 --- a/csf_tz/csf_tz/page/scan_qrcode/scan_qrcode.js +++ b/csf_tz/csf_tz/page/scan_qrcode/scan_qrcode.js @@ -1,46 +1,46 @@ +const SCANNER_LIBRARY_URL = "https://unpkg.com/html5-qrcode"; + frappe.pages["scan-qrcode"].on_page_load = function (wrapper) { - var page = frappe.ui.make_app_page({ + const page = frappe.ui.make_app_page({ parent: wrapper, title: "Scan QRCode", single_column: true, }); page.main.html(frappe.render_template("scan_qrcode", {})); - setTimeout(function () { - startScanner(); - }, 1000); + loadScannerLibrary() + .then(startScanner) + .catch(() => + frappe.msgprint(__("Could not load the QR scanner library (html5-qrcode). Check the internet connection.")) + ); }; -var lastResult, - countResults = 0; - -function startScanner() { - var resultContainer = document.getElementById("qr-reader-results"); +let lastResult; - var html5QrcodeScanner = new Html5QrcodeScanner("qr-reader", { - fps: 10, - qrbox: 250, +function loadScannerLibrary() { + if (window.Html5QrcodeScanner) { + return Promise.resolve(); + } + return new Promise((resolve, reject) => { + const script = document.createElement("script"); + script.src = SCANNER_LIBRARY_URL; + script.onload = resolve; + script.onerror = reject; + document.head.appendChild(script); }); - html5QrcodeScanner.render(onScanSuccess); } -function onScanSuccess(decodedText, decodedResult) { - if (decodedText !== lastResult) { - ++countResults; - lastResult = decodedText; - // Handle on success condition with the decoded message. - console.log(`Scan result ${decodedText}`, decodedResult); - sendApiCall(decodedText); - } +function startScanner() { + const scanner = new Html5QrcodeScanner("qr-reader", { fps: 10, qrbox: 250 }); + scanner.render(onScanSuccess); } -function sendApiCall(decodedText) { +function onScanSuccess(decodedText) { + if (decodedText === lastResult) { + return; + } + lastResult = decodedText; frappe.call({ method: "csf_tz.csf_tz.page.scan_qrcode.scan_qrcode.add_biometric_log", - args: { - data: decodedText, - }, - callback: function (r) { - console.log(r); - }, + args: { data: decodedText }, }); } From 9f8052408d5d4b3026bf27c27d5447b7573c5178 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:15 +0300 Subject: [PATCH 12/13] fix: remove Order Track tables with no doctype behind them --- .../doctype/order_track/order_track.json | 127 +----------------- 1 file changed, 1 insertion(+), 126 deletions(-) diff --git a/csf_tz/purchase_and_stock_management/doctype/order_track/order_track.json b/csf_tz/purchase_and_stock_management/doctype/order_track/order_track.json index 197ebffe..2b2aec1d 100644 --- a/csf_tz/purchase_and_stock_management/doctype/order_track/order_track.json +++ b/csf_tz/purchase_and_stock_management/doctype/order_track/order_track.json @@ -571,131 +571,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_order_progress", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Order Progress", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "order_progress", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "options": "Order Progress", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_items_ordered", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Items Ordered", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Items", - "length": 0, - "no_copy": 0, - "options": "Order Tracking Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -833,7 +708,7 @@ "read_only": 0, "read_only_onload": 0, "show_name_in_global_search": 0, - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "track_changes": 1, "track_seen": 0 From 9fed8fafc6ba4ec3ea483f5f39aa7080453169cb Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:51:16 +0300 Subject: [PATCH 13/13] fix: drop Order Track references to code that no longer exists --- .../doctype/order_track/order_track.js | 64 ++++--------------- 1 file changed, 14 insertions(+), 50 deletions(-) diff --git a/csf_tz/purchase_and_stock_management/doctype/order_track/order_track.js b/csf_tz/purchase_and_stock_management/doctype/order_track/order_track.js index fd9181bb..ddf07db6 100644 --- a/csf_tz/purchase_and_stock_management/doctype/order_track/order_track.js +++ b/csf_tz/purchase_and_stock_management/doctype/order_track/order_track.js @@ -2,61 +2,25 @@ // For license information, please see license.txt frappe.ui.form.on('Order Track', { - refresh: function(frm) { - frm.events.show_hide_fields(frm); - console.log(frm); - //console.log(hide_show_sections.name); - //alert(cur_frm.doc.docstatus) - - //make product inspection ie. submitted - if(cur_frm.doc.docstatus === 1 ) { - cur_frm.add_custom_button(__('Product Inspection'), function(){frm.events.make_product_inspection(frm)}, __("Make")); - - - } - + frm.events.show_hide_fields(frm); - - //Arrival date entered,clearing company and completion date ! blank - if (frm.doc.arrival_date && frm.doc.arrival_date != null ){ - if (frm.doc.clearing_company == '' || (frm.doc.expected_clearing_completion_date ==null)){ - var msg = "Either Clearing Company or Clearing Completion Date is unfilled,Please fill the fields"; - frappe.msgprint(msg); - throw msg; - - } + if (frm.doc.arrival_date && (!frm.doc.clearing_company || !frm.doc.expected_clearing_completion_date)) { + frappe.msgprint(__("Either Clearing Company or Clearing Completion Date is unfilled, please fill the fields")); } }, + show_hide_fields: function(frm) { + const has_supplier = Boolean(frm.doc.supplier && frm.doc.supplier_type); + frm.toggle_display('international_supplier', has_supplier && frm.doc.supplier_type == 'International Supplier'); + frm.toggle_display('section_status', has_supplier); + }, - show_hide_fields:function(frm){ - frm.toggle_display('section_international_supplier',(frm.doc.supplier && frm.doc.supplier_type && frm.doc.supplier_type=='International Supplier' )); - frm.toggle_display('section_containers',(frm.doc.supplier && frm.doc.supplier_type && frm.doc.supplier_type=='International Supplier')); - frm.toggle_display('section_local_supplier', (frm.doc.supplier && frm.doc.supplier_type && frm.doc.supplier_type=='Local Supplier')); - frm.toggle_display('section_order_progress',(frm.doc.supplier && frm.doc.supplier_type)); - frm.toggle_display('section_items_ordered', (frm.doc.supplier && frm.doc.supplier_type)); - frm.toggle_display('section_status', (frm.doc.supplier && frm.doc.supplier_type)); - }, - - - - supplier:function(frm){ - frm.events.show_hide_fields(frm); - - }, - - supplier_type:function(frm){ - frm.events.show_hide_fields(frm); - }, - - //Product Inspection function - make_product_inspection:function(){ - frappe.model.open_mapped_doc({ - method: "erpnext.purchase_and_stock_management.doctype.order_track.order_track.make_product_inspection", - frm: cur_frm - }) - - }, + supplier: function(frm) { + frm.events.show_hide_fields(frm); + }, + supplier_type: function(frm) { + frm.events.show_hide_fields(frm); + }, });