From 34d1559af921f8cabf7da109ad8b4cb401f1d9f5 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:02 +0300 Subject: [PATCH 01/34] fix: build item ageing from the v16 FIFO slots class --- .../warehouse_wise_item_balance_and_value.py | 78 +++++++------------ 1 file changed, 28 insertions(+), 50 deletions(-) diff --git a/csf_tz/csf_tz/report/warehouse_wise_item_balance_and_value/warehouse_wise_item_balance_and_value.py b/csf_tz/csf_tz/report/warehouse_wise_item_balance_and_value/warehouse_wise_item_balance_and_value.py index 3dc91c5f..a80a60d5 100644 --- a/csf_tz/csf_tz/report/warehouse_wise_item_balance_and_value/warehouse_wise_item_balance_and_value.py +++ b/csf_tz/csf_tz/report/warehouse_wise_item_balance_and_value/warehouse_wise_item_balance_and_value.py @@ -2,16 +2,10 @@ # For license information, please see license.txt import frappe -from erpnext.stock.report.stock_ageing.stock_ageing import get_average_age, get_fifo_queue -from erpnext.stock.report.stock_balance.stock_balance import ( - get_item_details, - get_item_warehouse_map, - get_items, - get_stock_ledger_entries, -) +from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, get_average_age +from erpnext.stock.report.stock_balance.stock_balance import StockBalanceReport from frappe import _ from frappe.utils import flt -from six import iteritems def execute(filters=None): @@ -21,41 +15,16 @@ def execute(filters=None): validate_filters(filters) columns = get_columns(filters) - - items = get_items(filters) - sle = get_stock_ledger_entries(filters, items) - - item_map = get_item_details(items, sle, filters) - iwb_map = get_item_warehouse_map(filters, sle) warehouse_list = get_warehouse_list(filters) - item_ageing = get_fifo_queue(filters) + item_ageing = FIFOSlots(filters).generate() + item_balance, item_value, item_groups = get_item_wise_balances(filters, warehouse_list) data = [] - item_balance = {} - item_value = {} - - for company, item, warehouse in sorted(iwb_map): - if not item_map.get(item): - continue - - row = [] - qty_dict = iwb_map[(company, item, warehouse)] - item_balance.setdefault((item, item_map[item]["item_group"]), []) - total_stock_value = 0.00 - for wh in warehouse_list: - row += [qty_dict.bal_qty] if wh.name in warehouse else [0.00] - total_stock_value += qty_dict.bal_val if wh.name in warehouse else 0.00 - - item_balance[(item, item_map[item]["item_group"])].append(row) - item_value.setdefault((item, item_map[item]["item_group"]), []) - item_value[(item, item_map[item]["item_group"])].append(total_stock_value) - # sum bal_qty by item - for (item, item_group), wh_balance in iteritems(item_balance): + for item, wh_balance in item_balance.items(): if not item_ageing.get(item): continue - total_stock_value = sum(item_value[(item, item_group)]) - row = [item, item_group, total_stock_value] + row = [item, item_groups[item], sum(item_value[item])] fifo_queue = item_ageing[item]["fifo_queue"] average_age = 0.00 @@ -64,24 +33,38 @@ def execute(filters=None): row += [average_age] - bal_qty = [sum(bal_qty) for bal_qty in zip(*wh_balance, strict=False)] + bal_qty = [sum(qty) for qty in zip(*wh_balance, strict=False)] total_qty = sum(bal_qty) if len(warehouse_list) > 1: row += [total_qty] row += bal_qty - if total_qty > 0: + if total_qty > 0 or not filters.get("filter_total_zero_qty"): data.append(row) - elif not filters.get("filter_total_zero_qty"): - data.append(row) - # frappe.msgprint("Debug start") - # frappe.msgprint(str(data)) + add_warehouse_column(columns, warehouse_list) check_zero_total_qty(columns, data) - # frappe.msgprint(str(columns) + " " + str(data)) return columns, data +def get_item_wise_balances(filters, warehouse_list): + """Group the ERPNext stock balance rows by item, one qty per warehouse column.""" + item_balance, item_value, item_groups = {}, {}, {} + report = StockBalanceReport(frappe._dict(filters, include_zero_stock_items=1)) + _columns, balances = report.run() + + for entry in sorted(balances, key=lambda d: (d.item_code, d.warehouse)): + row = [flt(entry.bal_qty) if wh.name == entry.warehouse else 0.00 for wh in warehouse_list] + total_stock_value = ( + flt(entry.bal_val) if entry.warehouse in [wh.name for wh in warehouse_list] else 0.00 + ) + item_balance.setdefault(entry.item_code, []).append(row) + item_value.setdefault(entry.item_code, []).append(total_stock_value) + item_groups[entry.item_code] = entry.item_group + + return item_balance, item_value, item_groups + + def get_columns(_filters): """return columns""" @@ -134,24 +117,19 @@ def add_warehouse_column(columns, warehouse_list): def check_zero_total_qty(columns, data): + """Drop warehouse columns whose quantity is zero on every row.""" zero_qty_columns = [] - # frappe.msgprint("Number of rows: " + str(len(data)) + " and number of columns " + str(len(columns))) for column_num in range(5, len(columns)): column_total = 0 for row_num in range(0, len(data)): - # frappe.msgprint("row " + str(row_num + 1) + " column " + str(column_num + 1) + " value" + str(data[row_num][column_num])) column_total += data[row_num][column_num] if column_total == 0: zero_qty_columns.append(column_num) - # frappe.msgprint("These columns should be removed: " + str(zero_qty_columns)) if len(zero_qty_columns) > 0: - # frappe.msgprint("Total number of columns to be deleted: " + str(len(zero_qty_columns))) index = 0 for col_num in zero_qty_columns: - # frappe.msgprint("Deleting column " + str(col_num - index)) for row in data: del row[col_num - index] del columns[col_num - index] index += 1 - # frappe.msgprint(str(columns) + " " + str(data)) From 32c76593e67c1de407e9c306ce0c346772bdc4a5 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:13 +0300 Subject: [PATCH 02/34] fix: drop the hard coded item group and brand defaults --- .../warehouse_wise_item_balance_and_value.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/csf_tz/csf_tz/report/warehouse_wise_item_balance_and_value/warehouse_wise_item_balance_and_value.js b/csf_tz/csf_tz/report/warehouse_wise_item_balance_and_value/warehouse_wise_item_balance_and_value.js index 457a9adb..588f9e96 100644 --- a/csf_tz/csf_tz/report/warehouse_wise_item_balance_and_value/warehouse_wise_item_balance_and_value.js +++ b/csf_tz/csf_tz/report/warehouse_wise_item_balance_and_value/warehouse_wise_item_balance_and_value.js @@ -25,16 +25,14 @@ frappe.query_reports["Warehouse wise Item Balance and Value"] = { "label": __("Item Group"), "fieldtype": "Link", "width": "80", - "options": "Item Group", - "default": "Vouchers" + "options": "Item Group" }, { "fieldname": "brand", "label": __("Brand"), "fieldtype": "Link", "width": "80", - "options": "Brand", - "default": "Halotel" + "options": "Brand" }, { "fieldname": "item_code", From 89bfe79d02bdab82a3332e0a49d997ea4118519a Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:14 +0300 Subject: [PATCH 03/34] fix: stop forcing an index that v16 no longer creates --- csf_tz/csf_tz/report/stock_balance_pro/stock_balance_pro.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/csf_tz/csf_tz/report/stock_balance_pro/stock_balance_pro.py b/csf_tz/csf_tz/report/stock_balance_pro/stock_balance_pro.py index 171c1d8b..ed8d387f 100644 --- a/csf_tz/csf_tz/report/stock_balance_pro/stock_balance_pro.py +++ b/csf_tz/csf_tz/report/stock_balance_pro/stock_balance_pro.py @@ -175,7 +175,7 @@ def get_stock_ledger_entries(filters, items): sle.company, sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference, sle.item_code as name, sle.voucher_no, sle.stock_value, 0 as excise_stock from - `tabStock Ledger Entry` sle force index (posting_sort_index) + `tabStock Ledger Entry` sle inner join `tabStock Entry` se on sle.voucher_type = "Stock Entry" and se.name = sle.voucher_no inner join `tabItem` i on sle.item_code = i.name where sle.is_cancelled = 0 @@ -188,7 +188,7 @@ def get_stock_ledger_entries(filters, items): sle.company, sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference, sle.item_code as name, sle.voucher_no, sle.stock_value, sle.actual_qty * si.excise_duty_applicable as excise_stock from - `tabStock Ledger Entry` sle force index (posting_sort_index) + `tabStock Ledger Entry` sle inner join `tabSales Invoice` si on sle.voucher_type = "Sales Invoice" and si.name = sle.voucher_no inner join `tabItem` i on sle.item_code = i.name where sle.is_cancelled = 0 @@ -200,7 +200,7 @@ def get_stock_ledger_entries(filters, items): sle.company, sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference, sle.item_code as name, sle.voucher_no, sle.stock_value, 0 as excise_stock from - `tabStock Ledger Entry` sle force index (posting_sort_index) + `tabStock Ledger Entry` sle inner join `tabItem` i on sle.item_code = i.name where sle.is_cancelled = 0 and sle.voucher_type NOT IN ("Stock Entry", "Sales Invoice") From 495f08626422aaf3bb5bea8a0be64b6be3fa11fb Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:19 +0300 Subject: [PATCH 04/34] fix: qualify the ambiguous item_code column --- .../stock_balance_pivot_warehouse.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/csf_tz/csf_tz/report/stock_balance_pivot_warehouse/stock_balance_pivot_warehouse.py b/csf_tz/csf_tz/report/stock_balance_pivot_warehouse/stock_balance_pivot_warehouse.py index c3616118..60291d1c 100644 --- a/csf_tz/csf_tz/report/stock_balance_pivot_warehouse/stock_balance_pivot_warehouse.py +++ b/csf_tz/csf_tz/report/stock_balance_pivot_warehouse/stock_balance_pivot_warehouse.py @@ -20,9 +20,8 @@ def execute(filters=None): for sle in stock_ledger_entry: row = [sle.item_code, sle.item_name, sle.brand, sle.item_group] - for e in warehouses: - row.append(sle_warehouse_map.get(sle.item_code, {}).get(e)) - # row += [total_qty] + warehouse_qty = [flt(sle_warehouse_map.get(sle.item_code, {}).get(e)) for e in warehouses] + row += warehouse_qty + [sum(warehouse_qty)] data.append(row) @@ -98,7 +97,7 @@ def get_sle_warehouse_map(stock_ledger_entry): """select sle.item_code, sle.warehouse, sum(actual_qty) from `tabStock Ledger Entry` sle inner join `tabItem` i on sle.item_code = i.item_code - where item_code in ({}) + where sle.item_code in ({}) group by i.item_name, i.brand, i.item_group, sle.warehouse""".format( ", ".join(["%s"] * len(stock_ledger_entry)) ), From d733c39e27f1a0a2861b2a8423cee3f3d875ca2e Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:23 +0300 Subject: [PATCH 05/34] fix: keep entries without a party and accept account lists --- .../general_ledger_pro/general_ledger_pro.py | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/csf_tz/csf_tz/report/general_ledger_pro/general_ledger_pro.py b/csf_tz/csf_tz/report/general_ledger_pro/general_ledger_pro.py index 9ead015c..98dc8be7 100644 --- a/csf_tz/csf_tz/report/general_ledger_pro/general_ledger_pro.py +++ b/csf_tz/csf_tz/report/general_ledger_pro/general_ledger_pro.py @@ -10,6 +10,7 @@ get_dimension_with_children, ) from erpnext.accounts.report.financial_statements import get_cost_centers_with_children +from erpnext.accounts.report.general_ledger.general_ledger import get_accounts_with_children from erpnext.accounts.report.utils import convert_to_presentation_currency, get_currency from erpnext.accounts.utils import get_account_currency from frappe import _, _dict @@ -45,6 +46,16 @@ def execute(filters=None): return columns, res +def parse_account_filter(account): + """The desk sends a JSON list; older callers send a single account name.""" + if isinstance(account, str): + try: + account = frappe.parse_json(account) + except Exception: + return [account] + return account if isinstance(account, list) else [account] + + def validate_filters(filters, account_details): if not filters.get("company"): frappe.throw(_("{0} is mandatory").format(_("Company"))) @@ -54,15 +65,16 @@ def validate_filters(filters, account_details): _("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))) ) - if filters.get("account") and not account_details.get(filters.account): - frappe.throw(_("Account {0} does not exists").format(filters.account)) + if filters.get("account"): + filters.account = parse_account_filter(filters.get("account")) + for account in filters.account: + if not account_details.get(account): + frappe.throw(_("Account {0} does not exists").format(account)) - if ( - filters.get("account") - and filters.get("group_by") == _("Group by Account") - and account_details[filters.account].is_group == 0 - ): - frappe.throw(_("Can not filter based on Account, if grouped by Account")) + if filters.get("account") and filters.get("group_by") == _("Group by Account"): + for account in filters.account: + if account_details[account].is_group == 0: + frappe.throw(_("Can not filter based on Account, if grouped by Account")) if filters.get("voucher_no") and filters.get("group_by") in [_("Group by Voucher")]: frappe.throw(_("Can not filter based on Voucher No, if grouped by Voucher")) @@ -95,7 +107,8 @@ def set_account_currency(filters): account_currency = None if filters.get("account"): - account_currency = get_account_currency(filters.account) + currencies = {get_account_currency(account) for account in filters.account} + account_currency = currencies.pop() if len(currencies) == 1 else None elif filters.get("party"): gle_currency = frappe.db.get_value( "GL Entry", @@ -191,13 +204,13 @@ def get_gl_entries(filters, accounting_dimensions): gl_entries_all_except_students = frappe.db.sql( f""" select - gle.name as gl_entry, posting_date, account, party_type, party, + `tabGL Entry`.name as gl_entry, posting_date, account, party_type, party, voucher_type, voucher_no, {dimension_fields} cost_center, project, against_voucher_type, against_voucher, account_currency, - remarks, against, is_opening, gle.creation {select_fields} - from `tabGL Entry` as gle - where party != 'Student' and company=%(company)s {get_conditions(filters)} + remarks, against, is_opening, `tabGL Entry`.creation {select_fields} + from `tabGL Entry` + where ifnull(party, '') != 'Student' and company=%(company)s {get_conditions(filters)} {distributed_cost_center_query} {order_by_statement} """, @@ -214,7 +227,7 @@ def get_gl_entries(filters, accounting_dimensions): gl_entries = (gl_entries_all_except_students or []) + (gl_entries_students or []) if filters.get("presentation_currency"): - return convert_to_presentation_currency(gl_entries, currency_map, filters.get("company")) + return convert_to_presentation_currency(gl_entries, currency_map, filters) else: return gl_entries @@ -226,11 +239,11 @@ def get_student_gl_entries( return frappe.db.sql( f""" select - gle.name as gl_entry, posting_date, account, party_type, CONCAT(std.first_name, " ", IFNULL(std.middle_name, ''), " ", IFNULL(std.last_name, '')) as party, + `tabGL Entry`.name as gl_entry, posting_date, account, party_type, CONCAT(std.first_name, " ", IFNULL(std.middle_name, ''), " ", IFNULL(std.last_name, '')) as party, voucher_type, voucher_no, {dimension_fields} cost_center, project, against_voucher_type, against_voucher, account_currency, - remarks, against, is_opening, gle.creation {select_fields} + remarks, against, is_opening, `tabGL Entry`.creation {select_fields} from `tabGL Entry` AS gle INNER JOIN `tabStudent` AS std ON gle.party = std.name where gle.party_type = 'Student' and company=%(company)s {get_conditions(filters)} @@ -245,11 +258,8 @@ def get_student_gl_entries( def get_conditions(filters): conditions = [] if filters.get("account"): - lft, rgt = frappe.db.get_value("Account", filters["account"], ["lft", "rgt"]) - conditions.append( - f"""account in (select name from tabAccount - where lft>={lft} and rgt<={rgt} and docstatus<2)""" - ) + filters.account = get_accounts_with_children(filters.account) + conditions.append("account in %(account)s") if filters.get("cost_center"): filters.cost_center = get_cost_centers_with_children(filters.cost_center) From d58a9e7faa016cc97cf74e027165fb4d5b1b3597 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:27 +0300 Subject: [PATCH 06/34] fix: guard the account filter setup on load --- .../multi_currency_ledger/multi_currency_ledger.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/csf_tz/csf_tz/report/multi_currency_ledger/multi_currency_ledger.js b/csf_tz/csf_tz/report/multi_currency_ledger/multi_currency_ledger.js index 2d62d6f5..54642b10 100644 --- a/csf_tz/csf_tz/report/multi_currency_ledger/multi_currency_ledger.js +++ b/csf_tz/csf_tz/report/multi_currency_ledger/multi_currency_ledger.js @@ -161,11 +161,4 @@ frappe.query_reports["Multi-Currency Ledger"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Multi-Currency Ledger"].filters.splice(15, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions("Multi-Currency Ledger", 15); From b1dd48365afa2336931b808c3ecc8ccfb368e4f2 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:28 +0300 Subject: [PATCH 07/34] fix: guard the ageing filter setup on load --- .../accounts_receivable_multi_currency.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csf_tz/csf_tz/report/accounts_receivable_multi_currency/accounts_receivable_multi_currency.js b/csf_tz/csf_tz/report/accounts_receivable_multi_currency/accounts_receivable_multi_currency.js index d672026a..93be86b8 100644 --- a/csf_tz/csf_tz/report/accounts_receivable_multi_currency/accounts_receivable_multi_currency.js +++ b/csf_tz/csf_tz/report/accounts_receivable_multi_currency/accounts_receivable_multi_currency.js @@ -199,4 +199,4 @@ frappe.query_reports["Accounts Receivable Multi Currency"] = { } } -erpnext.utils.add_dimensions('Accounts Receivable', 9); +erpnext.utils.add_dimensions('Accounts Receivable Multi Currency', 9); From 99270902fbd093a941e8075ca2881a892eba0f93 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:29 +0300 Subject: [PATCH 08/34] fix: require a company before querying receivable accounts --- .../accounts_receivable_utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/csf_tz/csf_tz/report/accounts_receivable_summary_multi_currency/accounts_receivable_utils.py b/csf_tz/csf_tz/report/accounts_receivable_summary_multi_currency/accounts_receivable_utils.py index cb40d9c0..46c5bb48 100644 --- a/csf_tz/csf_tz/report/accounts_receivable_summary_multi_currency/accounts_receivable_utils.py +++ b/csf_tz/csf_tz/report/accounts_receivable_summary_multi_currency/accounts_receivable_utils.py @@ -638,12 +638,18 @@ def prepare_conditions(self, party_type): where supplier_group=%s)""") values.append(self.filters.get("supplier_group")) + if not self.filters.get("company"): + frappe.throw(_("{0} is mandatory").format(_("Company"))) + accounts = [ d.name for d in frappe.get_all( "Account", filters={"account_type": account_type, "company": self.filters.company} ) ] + if not accounts: + frappe.throw(_("No {0} account found for {1}").format(_(account_type), self.filters.company)) + conditions.append("account in ({})".format(",".join(["%s"] * len(accounts)))) values += accounts From 4402f618ce19530b41705f9027955cc9e4f00463 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:34 +0300 Subject: [PATCH 09/34] fix: make company a required filter --- .../accounts_receivable_summary_multi_currency.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/csf_tz/csf_tz/report/accounts_receivable_summary_multi_currency/accounts_receivable_summary_multi_currency.js b/csf_tz/csf_tz/report/accounts_receivable_summary_multi_currency/accounts_receivable_summary_multi_currency.js index 4c1629c2..9ca34990 100644 --- a/csf_tz/csf_tz/report/accounts_receivable_summary_multi_currency/accounts_receivable_summary_multi_currency.js +++ b/csf_tz/csf_tz/report/accounts_receivable_summary_multi_currency/accounts_receivable_summary_multi_currency.js @@ -9,7 +9,8 @@ frappe.query_reports["Accounts Receivable Summary Multi Currency"] = { "label": __("Company"), "fieldtype": "Link", "options": "Company", - "default": frappe.defaults.get_user_default("Company") + "default": frappe.defaults.get_user_default("Company"), + "reqd": 1 }, { "fieldname":"ageing_based_on", From 3876f03f5ad9ab1bbdead32276e1b4b7528670ec Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:35 +0300 Subject: [PATCH 10/34] fix: read filters as a mapping rather than an int --- .../trial_balance_report_in_usd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csf_tz/csf_tz/report/trial_balance_report_in_usd/trial_balance_report_in_usd.py b/csf_tz/csf_tz/report/trial_balance_report_in_usd/trial_balance_report_in_usd.py index f422ee67..9786f157 100644 --- a/csf_tz/csf_tz/report/trial_balance_report_in_usd/trial_balance_report_in_usd.py +++ b/csf_tz/csf_tz/report/trial_balance_report_in_usd/trial_balance_report_in_usd.py @@ -92,10 +92,10 @@ def get_data(filters): filters.company, filters.from_date, filters.to_date, - min_lft, - max_rgt, filters, gl_entries_by_account, + root_lft=min_lft, + root_rgt=max_rgt, ignore_closing_entries=not flt(filters.with_period_closing_entry), ) From 68f175d2916a56700a2128865aa1f29d551eaa95 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:39 +0300 Subject: [PATCH 11/34] fix: default the fiscal year filter --- .../trial_balance_report_in_usd.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/csf_tz/csf_tz/report/trial_balance_report_in_usd/trial_balance_report_in_usd.js b/csf_tz/csf_tz/report/trial_balance_report_in_usd/trial_balance_report_in_usd.js index dd229939..85276b36 100644 --- a/csf_tz/csf_tz/report/trial_balance_report_in_usd/trial_balance_report_in_usd.js +++ b/csf_tz/csf_tz/report/trial_balance_report_in_usd/trial_balance_report_in_usd.js @@ -18,7 +18,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; @@ -95,12 +95,5 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "parent_field": "parent_account", "initial_depth": 3 }; - erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Trial Balance Eport in USD"].filters.splice(5, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); - }); + erpnext.utils.add_dimensions("Trial Balance Report in USD", 5); }); From 8e30ea240fff7a6fe5fd9ad5b4695ed634897ba4 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:40 +0300 Subject: [PATCH 12/34] fix: default the group by filter to invoice --- csf_tz/csf_tz/report/gross_profit_pro/gross_profit_pro.py | 1 + 1 file changed, 1 insertion(+) diff --git a/csf_tz/csf_tz/report/gross_profit_pro/gross_profit_pro.py b/csf_tz/csf_tz/report/gross_profit_pro/gross_profit_pro.py index e679b6ca..5bfcf4a3 100644 --- a/csf_tz/csf_tz/report/gross_profit_pro/gross_profit_pro.py +++ b/csf_tz/csf_tz/report/gross_profit_pro/gross_profit_pro.py @@ -13,6 +13,7 @@ def execute(filters=None): if not filters: filters = frappe._dict() + filters.group_by = filters.get("group_by") or "Invoice" filters.currency = frappe.get_cached_value("Company", filters.company, "default_currency") gross_profit_data = GrossProfitGenerator(filters) From 9eb88f8e283e1410ed34a5e4fe4855c08981b7b9 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:49 +0300 Subject: [PATCH 13/34] fix: return early without totals and read credit notes per row --- .../output_vat_reconciliation.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/csf_tz/csf_tz/report/output_vat_reconciliation/output_vat_reconciliation.py b/csf_tz/csf_tz/report/output_vat_reconciliation/output_vat_reconciliation.py index 5040adc1..e70967ed 100644 --- a/csf_tz/csf_tz/report/output_vat_reconciliation/output_vat_reconciliation.py +++ b/csf_tz/csf_tz/report/output_vat_reconciliation/output_vat_reconciliation.py @@ -11,6 +11,8 @@ def execute(filters=None): sales_label = {"details": "Sales - Sales Returns"} totals = {} generate_sales_returns(filters, data, totals, sales_label) + if not totals: + return columns, data data[0]["std_sales"] = fmt_money(float(totals["total_std_sales"]), 2, data[1]["invoice_currency"]) data[0]["vat"] = fmt_money(float(totals["vat"]), 2, data[1]["invoice_currency"]) data[0]["ex_amount"] = fmt_money(float(totals["ex_amount"]), 2, data[1]["invoice_currency"]) @@ -34,10 +36,9 @@ def generate_credit_note(data, totals, credit_note_label): if data[i]["details"] != "Credit Note - Sales Returns": credit_notes = frappe.get_list( "Sales Invoice", - filters={"is_return": 1, "return_against": "ACC-SINV-2019-07382", "docstatus": 1}, + filters={"is_return": 1, "return_against": data[i]["details"], "docstatus": 1}, fields=["*"], ) - print(credit_notes) for ii in credit_notes: if i == 1: data.append(credit_note_label) @@ -79,11 +80,16 @@ def generate_credit_note(data, totals, credit_note_label): def generate_sales_returns(filters, data, totals, sales_label): - efd_z_report_invoices = frappe.get_list( - "EFD Z Report Invoice", filters={"parent": filters.get("efd_report")}, fields=["*"] + efd_z_report_invoices = frappe.get_all( + "EFD Z Report Invoice", + filters={"parent": filters.get("efd_report"), "parenttype": "EFD Z Report"}, + fields=["invoice_number"], + parent_doctype="EFD Z Report", + order_by="idx", ) for idx, i in enumerate(efd_z_report_invoices): sales_invoice = frappe.get_doc("Sales Invoice", i.invoice_number).__dict__ + i.invoice_currency = sales_invoice["currency"] if idx == 0: data.append(sales_label) totals["total_std_sales"] = ( From 9773872f3b8d2589807ff2c327b798d2b208ba3a Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:52:55 +0300 Subject: [PATCH 14/34] fix: accept a numeric docstatus filter --- .../report/salary_register_csf/salary_register_csf.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/csf_tz/csf_tz/report/salary_register_csf/salary_register_csf.py b/csf_tz/csf_tz/report/salary_register_csf/salary_register_csf.py index 3bbf5be0..389ab7c7 100644 --- a/csf_tz/csf_tz/report/salary_register_csf/salary_register_csf.py +++ b/csf_tz/csf_tz/report/salary_register_csf/salary_register_csf.py @@ -5,7 +5,7 @@ import erpnext import frappe from frappe import _ -from frappe.utils import flt +from frappe.utils import cint, flt from frappe.utils.nestedset import get_descendants_of @@ -386,7 +386,9 @@ def get_salary_slips(filters): query = frappe.qb.from_(salary_slip).select(salary_slip.star) if filters.get("docstatus"): - query = query.where(salary_slip.docstatus == doc_status[filters.get("docstatus")]) + query = query.where( + salary_slip.docstatus == doc_status.get(filters.get("docstatus"), cint(filters.get("docstatus"))) + ) if filters.get("from_date"): query = query.where(salary_slip.start_date >= filters.get("from_date")) From 99a726c8abaf76af6c9373e88174a0899f03afa1 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:03 +0300 Subject: [PATCH 15/34] fix: accept a numeric docstatus filter --- .../report/salary_register_ctc/salary_register_ctc.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/csf_tz/csf_tz/report/salary_register_ctc/salary_register_ctc.py b/csf_tz/csf_tz/report/salary_register_ctc/salary_register_ctc.py index 87bfc163..53897d00 100644 --- a/csf_tz/csf_tz/report/salary_register_ctc/salary_register_ctc.py +++ b/csf_tz/csf_tz/report/salary_register_ctc/salary_register_ctc.py @@ -4,7 +4,7 @@ import erpnext import frappe from frappe import _ -from frappe.utils import flt +from frappe.utils import cint, flt from frappe.utils.nestedset import get_descendants_of @@ -187,7 +187,9 @@ def get_conditions(filters, company_currency): doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2} if filters.get("docstatus"): - conditions += "docstatus = {}".format(doc_status[filters.get("docstatus")]) + conditions += "docstatus = {}".format( + doc_status.get(filters.get("docstatus"), cint(filters.get("docstatus"))) + ) if filters.get("from_date"): conditions += " and start_date >= %(from_date)s" From 856b37f829efef41055a892dad3d0c3303059c9f Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:09 +0300 Subject: [PATCH 16/34] fix: accept a numeric docstatus filter --- .../salary_register_summary/salary_register_summary.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/csf_tz/csf_tz/report/salary_register_summary/salary_register_summary.py b/csf_tz/csf_tz/report/salary_register_summary/salary_register_summary.py index f7b595b1..068af67e 100644 --- a/csf_tz/csf_tz/report/salary_register_summary/salary_register_summary.py +++ b/csf_tz/csf_tz/report/salary_register_summary/salary_register_summary.py @@ -4,6 +4,7 @@ import erpnext import frappe from frappe import _ +from frappe.utils import cint from frappe.utils.nestedset import get_descendants_of @@ -143,7 +144,9 @@ def get_conditions(filters, company_currency): doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2} if filters.get("docstatus"): - conditions += "docstatus = {}".format(doc_status[filters.get("docstatus")]) + conditions += "docstatus = {}".format( + doc_status.get(filters.get("docstatus"), cint(filters.get("docstatus"))) + ) if filters.get("from_date"): conditions += " and start_date >= %(from_date)s" From d6dcaccb601607cc24e4abe5cab95432df9d26d9 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:13 +0300 Subject: [PATCH 17/34] fix: accept a numeric docstatus filter --- .../salary_register_summary_with_components.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/csf_tz/csf_tz/report/salary_register_summary_with_components/salary_register_summary_with_components.py b/csf_tz/csf_tz/report/salary_register_summary_with_components/salary_register_summary_with_components.py index dcef2d71..430fc087 100644 --- a/csf_tz/csf_tz/report/salary_register_summary_with_components/salary_register_summary_with_components.py +++ b/csf_tz/csf_tz/report/salary_register_summary_with_components/salary_register_summary_with_components.py @@ -4,6 +4,7 @@ import erpnext import frappe from frappe import _ +from frappe.utils import cint from frappe.utils.nestedset import get_descendants_of @@ -135,7 +136,9 @@ def get_conditions(filters, company_currency): doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2} if filters.get("docstatus"): - conditions += "docstatus = {}".format(doc_status[filters.get("docstatus")]) + conditions += "docstatus = {}".format( + doc_status.get(filters.get("docstatus"), cint(filters.get("docstatus"))) + ) if filters.get("from_date"): conditions += " and start_date >= %(from_date)s" From eb8b7387a39e2333b1c634ce8857572416d8a25a Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:19 +0300 Subject: [PATCH 18/34] fix: accept a numeric docstatus filter --- .../employee_salary_register_with_monthly_comparison.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/csf_tz/csf_tz/report/employee_salary_register_with_monthly_comparison/employee_salary_register_with_monthly_comparison.py b/csf_tz/csf_tz/report/employee_salary_register_with_monthly_comparison/employee_salary_register_with_monthly_comparison.py index 2cdee406..6898e79a 100644 --- a/csf_tz/csf_tz/report/employee_salary_register_with_monthly_comparison/employee_salary_register_with_monthly_comparison.py +++ b/csf_tz/csf_tz/report/employee_salary_register_with_monthly_comparison/employee_salary_register_with_monthly_comparison.py @@ -6,7 +6,7 @@ import frappe from erpnext import get_company_currency from frappe import _, msgprint -from frappe.utils import cstr, flt, getdate +from frappe.utils import cint, cstr, flt, getdate from frappe.utils.nestedset import get_descendants_of @@ -245,7 +245,9 @@ def get_cur_conditions(filters, company_currency): doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2} if filters.get("docstatus"): - conditions += "docstatus = {}".format(doc_status[filters.get("docstatus")]) + conditions += "docstatus = {}".format( + doc_status.get(filters.get("docstatus"), cint(filters.get("docstatus"))) + ) if filters.get("from_date"): conditions += " and start_date >= %(from_date)s" From 1ffa6e172ce10594e842df853a4be9d9c3fa40dc Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:24 +0300 Subject: [PATCH 19/34] fix: read the payroll cheque fields from custom fields --- csf_tz/csf_tz/report/bank_report/bank_report.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csf_tz/csf_tz/report/bank_report/bank_report.json b/csf_tz/csf_tz/report/bank_report/bank_report.json index aab0d9a6..da1a1728 100644 --- a/csf_tz/csf_tz/report/bank_report/bank_report.json +++ b/csf_tz/csf_tz/report/bank_report/bank_report.json @@ -9,13 +9,13 @@ "filters": [], "idx": 0, "is_standard": "Yes", - "modified": "2023-12-20 18:01:54.064621", + "modified": "2026-08-25 07:35:01.417396", "modified_by": "Administrator", "module": "CSF TZ", "name": "Bank Report", "owner": "Administrator", "prepared_report": 0, - "query": "SELECT\tdate_format(ss.start_date, '%%M %%Y') AS \"Month::140\",\n\t\tss.employee\t\tAS \"Employee ID::120\", \n\t\tss.employee_name\tAS \"Employee Name::200\", \n\t\tss.bank_account_no\tAS \"Account No.::120\", \n\t\temp.bank_code\t\tAS \"Bank Code.::120\", \n\t\tss.net_pay\t\tAS \"Net Pay:Currency:120\",\n\t\tconcat(cmp.abbr, '/', date_format(ss.start_date, '%%M %%Y/%%d')) AS \"Payroll Entry::140\",\n\t\tpe.cheque_number\tAS \"Cheque No.::120\", \n\t\tpe.cheque_date\t\tAS \"Cheque Date::120\", \n\t\tss.company\t\tAS \"Company::120\",\n\t\tb.bank AS \"Bank Name::120\",\n\t\tb.branch_code AS \"Branch::120\",\n\t\tadr.address_line1 AS \"Address1::120\",\n\t\tadr.city AS \"City::120\",\n\t\tadr.country AS \"Country::120\"\nFROM \t`tabSalary Slip` ss\n\tLEFT JOIN `tabEmployee` emp ON ss.employee = emp.name\n\tLEFT JOIN `tabPayroll Entry` pe ON ss.payroll_entry = pe.name\n\tLEFT OUTER JOIN `tabCompany` cmp ON ss.company = cmp.name \n\tLEFT JOIN `tabBank Account` b ON pe.bank_account_for_transfer = b.name\n\tLEFT JOIN `tabAddress` adr ON pe.bank_account_for_transfer = adr.address_title\nWHERE\temp.salary_mode = 'Bank'\nAND \tss.start_date >= %(from_date)s\nAND \tss.end_date <= %(to_date)s", + "query": "SELECT\tdate_format(ss.start_date, '%%M %%Y') AS \"Month::140\",\n\t\tss.employee\t\tAS \"Employee ID::120\", \n\t\tss.employee_name\tAS \"Employee Name::200\", \n\t\tss.bank_account_no\tAS \"Account No.::120\", \n\t\temp.bank_code\t\tAS \"Bank Code.::120\", \n\t\tss.net_pay\t\tAS \"Net Pay:Currency:120\",\n\t\tconcat(cmp.abbr, '/', date_format(ss.start_date, '%%M %%Y/%%d')) AS \"Payroll Entry::140\",\n\t\tpe.cheque_number\tAS \"Cheque No.::120\", \n\t\tpe.cheque_date\t\tAS \"Cheque Date::120\", \n\t\tss.company\t\tAS \"Company::120\",\n\t\tb.bank AS \"Bank Name::120\",\n\t\tb.branch_code AS \"Branch::120\",\n\t\tadr.address_line1 AS \"Address1::120\",\n\t\tadr.city AS \"City::120\",\n\t\tadr.country AS \"Country::120\"\nFROM \t`tabSalary Slip` ss\n\tLEFT JOIN `tabEmployee` emp ON ss.employee = emp.name\n\tLEFT JOIN `tabPayroll Entry` pe ON ss.payroll_entry = pe.name\n\tLEFT OUTER JOIN `tabCompany` cmp ON ss.company = cmp.name \n\tLEFT JOIN `tabBank Account` b ON pe.bank_account = b.name\n\tLEFT JOIN `tabAddress` adr ON pe.bank_account = adr.address_title\nWHERE\temp.salary_mode = 'Bank'\nAND \tss.start_date >= %(from_date)s\nAND \tss.end_date <= %(to_date)s", "ref_doctype": "Salary Slip", "report_name": "Bank Report", "report_type": "Query Report", From 21077105e1e60e38027b3dc95e8e445939060b9a Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:25 +0300 Subject: [PATCH 20/34] feat: add the date filters the bank report query needs --- .../csf_tz/report/bank_report/bank_report.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 csf_tz/csf_tz/report/bank_report/bank_report.js diff --git a/csf_tz/csf_tz/report/bank_report/bank_report.js b/csf_tz/csf_tz/report/bank_report/bank_report.js new file mode 100644 index 00000000..7006c740 --- /dev/null +++ b/csf_tz/csf_tz/report/bank_report/bank_report.js @@ -0,0 +1,22 @@ +// Copyright (c) 2016, Aakvatech and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Bank Report"] = { + "filters": [ + { + "fieldname": "from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.month_start(), + "reqd": 1, + }, + { + "fieldname": "to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.month_end(), + "reqd": 1, + }, + ] +}; From 8b835e3354429f23417c03b35d502a4f54a86c5b Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:26 +0300 Subject: [PATCH 21/34] feat: add payroll entry cheque fields for the bank report --- .../16_payroll_entry_cheque.json | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 csf_tz/patches/custom_fields/custom_fields_json/16_payroll_entry_cheque.json diff --git a/csf_tz/patches/custom_fields/custom_fields_json/16_payroll_entry_cheque.json b/csf_tz/patches/custom_fields/custom_fields_json/16_payroll_entry_cheque.json new file mode 100644 index 00000000..3624e3e9 --- /dev/null +++ b/csf_tz/patches/custom_fields/custom_fields_json/16_payroll_entry_cheque.json @@ -0,0 +1,26 @@ +[ + { + "dt": "Payroll Entry", + "fieldname": "bank_payment_details", + "fieldtype": "Section Break", + "insert_after": "payroll_payable_account", + "label": "Bank Payment Details", + "name": "Payroll Entry-bank_payment_details" + }, + { + "dt": "Payroll Entry", + "fieldname": "cheque_number", + "fieldtype": "Data", + "insert_after": "bank_payment_details", + "label": "Cheque Number", + "name": "Payroll Entry-cheque_number" + }, + { + "dt": "Payroll Entry", + "fieldname": "cheque_date", + "fieldtype": "Date", + "insert_after": "cheque_number", + "label": "Cheque Date", + "name": "Payroll Entry-cheque_date" + } +] From b1ea98c28bf395c8c42a8ca0acdd11188532a9e6 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:26 +0300 Subject: [PATCH 22/34] fix: join custom fields on dt instead of parent --- .../parent_child_relationship/parent_child_relationship.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csf_tz/csf_tz/report/parent_child_relationship/parent_child_relationship.json b/csf_tz/csf_tz/report/parent_child_relationship/parent_child_relationship.json index 5a068992..dfe96e76 100644 --- a/csf_tz/csf_tz/report/parent_child_relationship/parent_child_relationship.json +++ b/csf_tz/csf_tz/report/parent_child_relationship/parent_child_relationship.json @@ -34,13 +34,13 @@ ], "idx": 0, "is_standard": "Yes", - "modified": "2021-08-21 21:42:36.003335", + "modified": "2026-08-25 07:35:01.417396", "modified_by": "Administrator", "module": "CSF TZ", "name": "Parent Child Relationship", "owner": "Administrator", "prepared_report": 0, - "query": "SELECT CONCAT_WS('-', dt.module, dt.name) as name, GROUP_CONCAT(df.options) as imports\nFROM `tabDocType` dt\nINNER JOIN `tabDocField` df ON dt.name = df.parent\nWHERE df.options IS NOT NULL\n AND df.fieldtype = \"Link\"\n AND dt.is_submittable = %(is_submittable)s\n AND dt.istable = %(is_table)s\n AND dt.module = %(module)s\nGROUP BY dt.module, dt.name\nUNION ALL\nSELECT CONCAT_WS('-', dt.module, dt.name) as name, GROUP_CONCAT(df.options) as imports\nFROM `tabDocType` dt\nINNER JOIN `tabCustom Field` df ON dt.name = df.parent\nWHERE df.options IS NOT NULL\n AND df.fieldtype = \"Link\"\n AND dt.is_submittable = %(is_submittable)s\n AND dt.istable = %(is_table)s\n AND dt.module = %(module)s\nGROUP BY dt.module, dt.name", + "query": "SELECT CONCAT_WS('-', dt.module, dt.name) as name, GROUP_CONCAT(df.options) as imports\nFROM `tabDocType` dt\nINNER JOIN `tabDocField` df ON dt.name = df.parent\nWHERE df.options IS NOT NULL\n AND df.fieldtype = \"Link\"\n AND dt.is_submittable = %(is_submittable)s\n AND dt.istable = %(is_table)s\n AND dt.module = %(module)s\nGROUP BY dt.module, dt.name\nUNION ALL\nSELECT CONCAT_WS('-', dt.module, dt.name) as name, GROUP_CONCAT(df.options) as imports\nFROM `tabDocType` dt\nINNER JOIN `tabCustom Field` df ON dt.name = df.dt\nWHERE df.options IS NOT NULL\n AND df.fieldtype = \"Link\"\n AND dt.is_submittable = %(is_submittable)s\n AND dt.istable = %(is_table)s\n AND dt.module = %(module)s\nGROUP BY dt.module, dt.name", "ref_doctype": "DocType", "report_name": "Parent Child Relationship", "report_type": "Query Report", From e4afd3e6bd940971e29fe822ad134153c796835c Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:27 +0300 Subject: [PATCH 23/34] fix: select only invoice fields the report may read --- .../withholding_tax_payment_summary.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/csf_tz/csf_tz/report/withholding_tax_payment_summary/withholding_tax_payment_summary.py b/csf_tz/csf_tz/report/withholding_tax_payment_summary/withholding_tax_payment_summary.py index b7594a86..546159a8 100644 --- a/csf_tz/csf_tz/report/withholding_tax_payment_summary/withholding_tax_payment_summary.py +++ b/csf_tz/csf_tz/report/withholding_tax_payment_summary/withholding_tax_payment_summary.py @@ -6,6 +6,9 @@ def execute(filters=None): + if not frappe.get_meta("Sales Invoice").has_field("lease_item"): + frappe.throw(_("Custom field Sales Invoice.lease_item is not installed on this site")) + rental = filters.get("rental") columns, data = get_columns(rental), [] From 701e5331c1c2707db45e1d2514563320603f4491 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:32 +0300 Subject: [PATCH 24/34] feat: add the date filters the withholding query needs --- .../withholding_tax_summary_on_sales.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/csf_tz/csf_tz/report/withholding_tax_summary_on_sales/withholding_tax_summary_on_sales.js b/csf_tz/csf_tz/report/withholding_tax_summary_on_sales/withholding_tax_summary_on_sales.js index 61209a22..6c1994dc 100644 --- a/csf_tz/csf_tz/report/withholding_tax_summary_on_sales/withholding_tax_summary_on_sales.js +++ b/csf_tz/csf_tz/report/withholding_tax_summary_on_sales/withholding_tax_summary_on_sales.js @@ -8,13 +8,15 @@ frappe.query_reports["Withholding Tax Summary on Sales"] = { "fieldname": "from_date", "label": __("From Date"), "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_start_date"), + "default": frappe.defaults.get_user_default("year_start_date") || frappe.datetime.year_start(), + "reqd": 1, }, { "fieldname": "to_date", "label": __("To Date"), "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_end_date"), + "default": frappe.defaults.get_user_default("year_end_date") || frappe.datetime.year_end(), + "reqd": 1, }, ] }; From f52356f241b75324b8efd02896bfa13916e1e6ab Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:33 +0300 Subject: [PATCH 25/34] chore: remove the misspelled excise duty filter script --- .../excise_duty_report/exise_duty_report.js | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 csf_tz/csf_tz/report/excise_duty_report/exise_duty_report.js diff --git a/csf_tz/csf_tz/report/excise_duty_report/exise_duty_report.js b/csf_tz/csf_tz/report/excise_duty_report/exise_duty_report.js deleted file mode 100644 index a1e18207..00000000 --- a/csf_tz/csf_tz/report/excise_duty_report/exise_duty_report.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2016, Aakvatech and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Excise Duty Report"] = { - "filters": [ - { - "fieldname": "from_date", - "fieldtype": "Date", - "label": "From Date", - "mandatory": 1, - "wildcard_filter": 0 - }, - { - "fieldname": "to_date", - "fieldtype": "Date", - "label": "To Date", - "mandatory": 1, - "wildcard_filter": 0 - } - ] -} From cdcc47d3bbe096b4896085a17e0f3d376d49ec96 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:33 +0300 Subject: [PATCH 26/34] feat: add filters that load for the excise duty report --- .../excise_duty_report/excise_duty_report.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 csf_tz/csf_tz/report/excise_duty_report/excise_duty_report.js diff --git a/csf_tz/csf_tz/report/excise_duty_report/excise_duty_report.js b/csf_tz/csf_tz/report/excise_duty_report/excise_duty_report.js new file mode 100644 index 00000000..b1218c13 --- /dev/null +++ b/csf_tz/csf_tz/report/excise_duty_report/excise_duty_report.js @@ -0,0 +1,22 @@ +// Copyright (c) 2016, Aakvatech and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Excise Duty Report"] = { + "filters": [ + { + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "reqd": 1, + "default": frappe.datetime.month_start() + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "reqd": 1, + "default": frappe.datetime.get_today() + } + ] +} From 56dee346f8649c015fdae58c5272ab0f0db50e74 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:34 +0300 Subject: [PATCH 27/34] fix: default the excise duty detailed report dates --- .../excise_duty_detailed_report.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/csf_tz/csf_tz/report/excise_duty_detailed_report/excise_duty_detailed_report.js b/csf_tz/csf_tz/report/excise_duty_detailed_report/excise_duty_detailed_report.js index cf85cae2..76547df8 100644 --- a/csf_tz/csf_tz/report/excise_duty_detailed_report/excise_duty_detailed_report.js +++ b/csf_tz/csf_tz/report/excise_duty_detailed_report/excise_duty_detailed_report.js @@ -8,15 +8,15 @@ frappe.query_reports["Excise Duty Detailed Report"] = { "fieldname": "from_date", "fieldtype": "Date", "label": "From Date", - "mandatory": 1, - "wildcard_filter": 0 + "reqd": 1, + "default": frappe.datetime.month_start() }, { "fieldname": "to_date", "fieldtype": "Date", "label": "To Date", - "mandatory": 1, - "wildcard_filter": 0 + "reqd": 1, + "default": frappe.datetime.get_today() } ] } From fe565789311e0d8b6dedc726c4aa96f96273de58 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:35 +0300 Subject: [PATCH 28/34] fix: explain that the report needs the lending app --- .../report/loan_repayment_details/loan_repayment_details.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/csf_tz/csf_tz/report/loan_repayment_details/loan_repayment_details.py b/csf_tz/csf_tz/report/loan_repayment_details/loan_repayment_details.py index 454b7e23..c8d1a79b 100644 --- a/csf_tz/csf_tz/report/loan_repayment_details/loan_repayment_details.py +++ b/csf_tz/csf_tz/report/loan_repayment_details/loan_repayment_details.py @@ -7,6 +7,9 @@ def execute(filters=None): + if not frappe.db.exists("DocType", "Loan Repayment"): + frappe.throw(_("The Lending app is not installed on this site")) + loans = [] data = [] columns = get_columns(filters) From f386024c2d82913a4a5d82b42f247e5eacd3bd15 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:40 +0300 Subject: [PATCH 29/34] fix: explain that the report needs its source doctype --- .../customer_loan_assistance_report.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/csf_tz/sales_and_marketing/report/customer_loan_assistance_report/customer_loan_assistance_report.py b/csf_tz/sales_and_marketing/report/customer_loan_assistance_report/customer_loan_assistance_report.py index 9875d7f0..4f1e5a8b 100644 --- a/csf_tz/sales_and_marketing/report/customer_loan_assistance_report/customer_loan_assistance_report.py +++ b/csf_tz/sales_and_marketing/report/customer_loan_assistance_report/customer_loan_assistance_report.py @@ -6,6 +6,9 @@ def execute(filters=None): + if not frappe.db.exists("DocType", "Customer Loan Assistance"): + frappe.throw(_("DocType Customer Loan Assistance is not installed on this site")) + columns, data = [], [] columns = [ From 2152697a22436f004b11ee68027122e01954d176 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:44 +0300 Subject: [PATCH 30/34] fix: explain that the report needs the is_marked field --- .../items_marked_for_delivery/items_marked_for_delivery.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/csf_tz/sales_and_marketing/report/items_marked_for_delivery/items_marked_for_delivery.py b/csf_tz/sales_and_marketing/report/items_marked_for_delivery/items_marked_for_delivery.py index 56a977e9..46340e72 100644 --- a/csf_tz/sales_and_marketing/report/items_marked_for_delivery/items_marked_for_delivery.py +++ b/csf_tz/sales_and_marketing/report/items_marked_for_delivery/items_marked_for_delivery.py @@ -6,6 +6,9 @@ def execute(filters=None): + if not frappe.get_meta("Sales Invoice Item").has_field("is_marked"): + frappe.throw(_("Custom field Sales Invoice Item.is_marked is not installed on this site")) + columns, data = [], [] columns = [ {"fieldname": "customer_name", "label": _("Customer"), "fieldtype": "Data", "width": 150}, From 1ed41aa4252fcde89172eddef56314fa63c43550 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:49 +0300 Subject: [PATCH 31/34] fix: read the quotation party from party_name --- .../report/item_wise_leads_report/item_wise_leads_report.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/csf_tz/sales_and_marketing/report/item_wise_leads_report/item_wise_leads_report.py b/csf_tz/sales_and_marketing/report/item_wise_leads_report/item_wise_leads_report.py index adc2a32d..ecd18aed 100644 --- a/csf_tz/sales_and_marketing/report/item_wise_leads_report/item_wise_leads_report.py +++ b/csf_tz/sales_and_marketing/report/item_wise_leads_report/item_wise_leads_report.py @@ -40,15 +40,13 @@ def execute(filters=None): tqi.item_name, SUM(tqi.qty) AS total_qty, COUNT(tq.name) AS quotations, - COUNT(DISTINCT customer_quot.customer) AS customers, - COUNT(DISTINCT tq.lead) AS leads, + COUNT(DISTINCT CASE WHEN tq.quotation_to = 'Customer' THEN tq.party_name END) AS customers, + COUNT(DISTINCT CASE WHEN tq.quotation_to = 'Lead' THEN tq.party_name END) AS leads, tqi.warehouse FROM (`tabQuotation Item` tqi) LEFT JOIN (`tabQuotation` tq) ON (tqi.parent = tq.name) - LEFT JOIN - (`tabQuotation` customer_quot) ON tqi.parent = customer_quot.name AND customer_quot.quotation_to = 'Customer' WHERE tq.docstatus = 1 AND tq.transaction_date BETWEEN %(from_date)s AND %(to_date)s GROUP BY tqi.item_code, tqi.warehouse From 30afcac07e7f669f2fec6ce7be2b3cdf658baca0 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:53 +0300 Subject: [PATCH 32/34] fix: query shipment tracking from the Order Track doctype --- .../shipment_tracking/shipment_tracking.py | 171 ++++++------------ 1 file changed, 56 insertions(+), 115 deletions(-) diff --git a/csf_tz/purchase_and_stock_management/report/shipment_tracking/shipment_tracking.py b/csf_tz/purchase_and_stock_management/report/shipment_tracking/shipment_tracking.py index 5c962254..32e33f78 100644 --- a/csf_tz/purchase_and_stock_management/report/shipment_tracking/shipment_tracking.py +++ b/csf_tz/purchase_and_stock_management/report/shipment_tracking/shipment_tracking.py @@ -6,23 +6,67 @@ def execute(filters=None): - columns, data = [], [] + columns = get_columns() - columns = [ + if filters.from_date > filters.to_date: + frappe.throw(_("From Date must be before To Date {}").format(filters.to_date)) + + where_filter = { + "from_date": filters.from_date, + "to_date": filters.to_date, + } + where = "" + if filters.order: + where += " AND tot.name = %(order)s " + where_filter.update({"order": filters.order}) + + if filters.supplier: + where += " AND tot.supplier = %(supplier)s " + where_filter.update({"supplier": filters.supplier}) + + data = frappe.db.sql( + """SELECT + tot.name AS order_no, + tot.supplier, + tot.supplier_type, + tot.shipped_date, + tot.expected_arrival_date, + tot.mode_of_transport, + tot.bl_number, + tot.arrival_date, + tot.discharged_date, + tot.clearing_company, + tot.expected_clearing_completion_date, + tot.clearing_completion_date + FROM + `tabOrder Track` tot + WHERE + tot.expected_arrival_date BETWEEN %(from_date)s AND %(to_date)s + """ + + where, + where_filter, + as_dict=1, + ) + return columns, data + + +def get_columns(): + return [ { "fieldname": "order_no", "label": _("Order No"), "fieldtype": "Link", - "options": "Order Tracking", + "options": "Order Track", "width": 150, }, - {"fieldname": "project", "label": _("Project"), "fieldtype": "Link", "options": "Project"}, { "fieldname": "supplier", "label": _("Supplier"), "fieldtype": "Link", "options": "Supplier", + "width": 150, }, + {"fieldname": "supplier_type", "label": _("Supplier Type"), "fieldtype": "Data", "width": 150}, { "fieldname": "mode_of_transport", "label": _("Mode of Transport"), @@ -36,123 +80,20 @@ def execute(filters=None): "fieldtype": "Date", "width": 150, }, - { - "fieldname": "arrival_date", - "label": _("Arrival Date"), - "fieldtype": "Date", - }, - {"fieldname": "order_status", "label": _("Status"), "fieldtype": "Data", "width": 200}, + {"fieldname": "arrival_date", "label": _("Arrival Date"), "fieldtype": "Date", "width": 120}, + {"fieldname": "discharged_date", "label": _("Discharged Date"), "fieldtype": "Date", "width": 120}, {"fieldname": "bl_number", "label": _("Bl No"), "fieldtype": "Data", "width": 150}, - {"fieldname": "container_no", "label": _("Container"), "fieldtype": "Data", "width": 150}, - { - "fieldname": "container_size", - "label": _("Container Size"), - "fieldtype": "Data", - }, + {"fieldname": "clearing_company", "label": _("Clearing Company"), "fieldtype": "Data", "width": 150}, { - "fieldname": "no_of_packages", - "label": _("No of Packages"), - "fieldtype": "Data", + "fieldname": "expected_clearing_completion_date", + "label": _("Expected Clearing Completion Date"), + "fieldtype": "Date", + "width": 150, }, { "fieldname": "clearing_completion_date", "label": _("Clearing Completion Date"), "fieldtype": "Date", - }, - { - "fieldname": "delivered_date", - "label": _("Delivered Date"), - "fieldtype": "Date", - }, - { - "fieldname": "offloading_date", - "label": _("Off-Loading Date"), - "fieldtype": "Date", + "width": 150, }, ] - - if filters.from_date > filters.to_date: - frappe.throw(_("From Date must be before To Date {}").format(filters.to_date)) - - where_filter = { - "from_date": filters.from_date, - "to_date": filters.to_date, - } - where = "" - if filters.order: - where += " AND tot.name = %(order)s " - where_filter.update({"order": filters.order}) - - if filters.supplier: - where += " AND tot.supplier = %(supplier)s " - where_filter.update({"supplier": filters.supplier}) - - data = frappe.db.sql( - """SELECT - tot.name AS order_no, - tot.supplier, - tot.project, - tot.shipped_date, - tot.expected_arrival_date, - tot.mode_of_transport, - tot.bl_number, - tot.arrival_date, - tot.clearing_completion_date, - tot.delivered_date, - tot.offloading_date, - /*tc.container_no, - tc.size, - tc.no_of_packages,*/ - - (SELECT - CONCAT(op.date, " : ", op.current_location, ":", op.status) - FROM - `tabOrder Progress` AS op - WHERE - tot.name = op.parent - ORDER BY - op.date DESC - LIMIT 0,1 - ) AS order_status - FROM - (`tabOrder Tracking` tot) - /*LEFT JOIN - (`tabContainer` tc) - ON (tot.name = tc.parent)*/ - Where - tot.expected_arrival_date BETWEEN %(from_date)s AND %(to_date)s - """ - + where, - where_filter, - as_dict=1, - ) - - for order in data: - # - # For container info - # - container_info = frappe.db.sql( - """SELECT - container_no, size,no_of_packages - FROM - (`tabContainer` tc) - LEFT JOIN - (`tabOrder Tracking` tot) - ON (tot.name = tc.parent) - WHERE - tot.name = %(parent)s """, - { - "parent": order.order_no, - }, - as_dict=1, - ) - order.container_no = "" - order.container_size = "" - order.no_of_packages = "" - - for co in container_info: - order.container_no += str(co.container_no) + "," - order.container_size += str(co.size) + "," - order.no_of_packages += str(co.no_of_packages) + "," - - return columns, data From 59a30e5ce6be4428dc18dc15defc62c59d274b65 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:58 +0300 Subject: [PATCH 33/34] fix: point shipment tracking at the Order Track doctype --- .../report/shipment_tracking/shipment_tracking.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csf_tz/purchase_and_stock_management/report/shipment_tracking/shipment_tracking.json b/csf_tz/purchase_and_stock_management/report/shipment_tracking/shipment_tracking.json index 275dc176..78e7cabc 100644 --- a/csf_tz/purchase_and_stock_management/report/shipment_tracking/shipment_tracking.json +++ b/csf_tz/purchase_and_stock_management/report/shipment_tracking/shipment_tracking.json @@ -6,12 +6,12 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2019-05-23 15:29:37.527392", + "modified": "2026-08-25 07:35:01.417396", "modified_by": "Administrator", "module": "Purchase And Stock Management", "name": "Shipment Tracking", "owner": "Administrator", - "ref_doctype": "Order Tracking", + "ref_doctype": "Order Track", "report_name": "Shipment Tracking", "report_type": "Script Report", "roles": [ From 4a85bad67e0a89d14fc75f6026767efdc8d0b5a3 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 26 Aug 2026 11:53:59 +0300 Subject: [PATCH 34/34] fix: filter shipment tracking by Order Track orders --- .../report/shipment_tracking/shipment_tracking.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csf_tz/purchase_and_stock_management/report/shipment_tracking/shipment_tracking.js b/csf_tz/purchase_and_stock_management/report/shipment_tracking/shipment_tracking.js index 12968db9..ac6a8bae 100644 --- a/csf_tz/purchase_and_stock_management/report/shipment_tracking/shipment_tracking.js +++ b/csf_tz/purchase_and_stock_management/report/shipment_tracking/shipment_tracking.js @@ -25,7 +25,7 @@ frappe.query_reports["Shipment Tracking"] = { "fieldname":"order", "label": __("Order"), "fieldtype": "Link", - "options": "Order Tracking", + "options": "Order Track", }, /* { "fieldname":"purchase_order",