From 0fdec8fac80e011150f230d3b4130161bcf97401 Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Thu, 14 Jul 2022 18:09:08 -0400 Subject: [PATCH 01/26] pref: reduce count of db calls from n to 2 (cherry picked from commit 73ade04dcf8a4ab31fc803f12a430e417da6967a) --- .../warehouse_wise_stock_value.py | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py index dbf6cf05e79..4245d7d4103 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -30,26 +30,23 @@ def get( warehouse_filters.append(["company", "=", filters.get("company")]) warehouses = frappe.get_list( - "Warehouse", fields=["name"], filters=warehouse_filters, order_by="name" + "Warehouse", pluck="name", filters=warehouse_filters, order_by="name" ) - for wh in warehouses: - balance = get_stock_value_from_bin(warehouse=wh.name) - wh["balance"] = balance[0][0] - - warehouses = [x for x in warehouses if not (x.get("balance") == None)] + warehouses = frappe.get_list( + "Bin", + fields=["warehouse", "stock_value"], + filters={"warehouse": ["IN", warehouses], "stock_value": [">", 0]}, + order_by="stock_value DESC", + limit_page_length=10 + ) if not warehouses: return [] - sorted_warehouse_map = sorted(warehouses, key=lambda i: i["balance"], reverse=True) - - if len(sorted_warehouse_map) > 10: - sorted_warehouse_map = sorted_warehouse_map[:10] - - for warehouse in sorted_warehouse_map: - labels.append(_(warehouse.get("name"))) - datapoints.append(warehouse.get("balance")) + for warehouse in warehouses: + labels.append(_(warehouse.get("warehouse"))) + datapoints.append(warehouse.get("stock_value")) return { "labels": labels, From c9443123f90d57e358b08fca969d702f4c0a80bb Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Thu, 14 Jul 2022 18:09:52 -0400 Subject: [PATCH 02/26] chore: remove unused import (cherry picked from commit bc3023318ec329b243ec231f190865e4d91e8723) --- .../warehouse_wise_stock_value/warehouse_wise_stock_value.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py index 4245d7d4103..5bb6446101c 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -6,8 +6,6 @@ import frappe from frappe import _ from frappe.utils.dashboard import cache_source -from erpnext.stock.utils import get_stock_value_from_bin - @frappe.whitelist() @cache_source @@ -38,7 +36,7 @@ def get( fields=["warehouse", "stock_value"], filters={"warehouse": ["IN", warehouses], "stock_value": [">", 0]}, order_by="stock_value DESC", - limit_page_length=10 + limit_page_length=10, ) if not warehouses: From 18d93f83982fb1b3b3b76a0b3d19e76fb8d94d69 Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Thu, 14 Jul 2022 18:53:50 -0400 Subject: [PATCH 03/26] fix: sum stock_value and group by warehouse (cherry picked from commit 1e20358c28d3929dd862feb05025ee566a4e2285) --- .../warehouse_wise_stock_value/warehouse_wise_stock_value.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py index 5bb6446101c..d488150eef3 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -33,8 +33,9 @@ def get( warehouses = frappe.get_list( "Bin", - fields=["warehouse", "stock_value"], + fields=["warehouse", "sum(stock_value) stock_value"], filters={"warehouse": ["IN", warehouses], "stock_value": [">", 0]}, + group_by="warehouse", order_by="stock_value DESC", limit_page_length=10, ) From 08c69c7a762dce43f150ea6192e775f5bdebbd0b Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Fri, 29 Jul 2022 14:22:31 +0530 Subject: [PATCH 04/26] fix: (india) HSN wise report Problem: The previous approach for calculating tax_rate was incorrect. ``` ['tax_rate'] * ['number of unique rows'] ``` Joining `tabSales Taxes and Charges` was adding unnecessary rows & complexity. Solution: Instead of trying to get tax_rate from the main query itself, I used the get_tax_accounts's data to calculate the correct tax_rate. Todo: Union Territory --- .../hsn_wise_summary_of_outward_supplies.py | 93 ++++++++++++------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py index f171b6e9cbe..95b8e715bee 100644 --- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py +++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py @@ -23,22 +23,29 @@ def _execute(filters=None): if not filters: filters = {} columns = get_columns() + output_gst_accounts = get_output_gst_accounts(filters.company) company_currency = erpnext.get_company_currency(filters.company) item_list = get_items(filters) if item_list: - itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency) + itemised_tax, tax_columns = get_tax_accounts( + item_list, columns, company_currency, output_gst_accounts + ) data = [] added_item = [] for d in item_list: if (d.parent, d.item_code) not in added_item: - row = [d.gst_hsn_code, d.description, d.stock_uom, d.stock_qty, d.tax_rate or 0] + row = [d.gst_hsn_code, d.description, d.stock_uom, d.stock_qty] total_tax = 0 + tax_rate = 0 for tax in tax_columns: item_tax = itemised_tax.get((d.parent, d.item_code), {}).get(tax, {}) + if item_tax.get("is_gst_tax"): + tax_rate += flt(item_tax.get("tax_rate", 0)) total_tax += flt(item_tax.get("tax_amount", 0)) + row += [tax_rate] row += [d.base_net_amount + total_tax] row += [d.base_net_amount] for tax in tax_columns: @@ -51,6 +58,40 @@ def _execute(filters=None): return columns, data +def get_output_gst_accounts(company): + + accounts = frappe.qb.DocType("Account") + gst_accounts = frappe.qb.DocType("GST Account") + + accounts_query = ( + frappe.qb.from_(accounts) + .select(accounts.name) + .where((accounts.account_type == "Tax") & (accounts.root_type == "Liability")) + ) + + gst_accounts_query = ( + frappe.qb.from_(gst_accounts) + .select( + gst_accounts.cgst_account, + gst_accounts.sgst_account, + gst_accounts.igst_account, + gst_accounts.utgst_account, + gst_accounts.cess_account, + ) + .where((gst_accounts.is_reverse_charge_account == 0) & (gst_accounts.company == company)) + ) + + gst_accounts_list = [ + account for sublist in gst_accounts_query.run() for account in sublist if account + ] + + tax_accounts_list = [account[0] for account in accounts_query.run() if account] + + output_tax_list = [account for account in gst_accounts_list if account in tax_accounts_list] + + return output_tax_list + + def get_columns(): columns = [ { @@ -103,40 +144,19 @@ def get_items(filters): SELECT `tabSales Invoice Item`.gst_hsn_code, `tabSales Invoice Item`.stock_uom, - sum( - `tabSales Invoice Item`.stock_qty - ) as stock_qty, - sum( - `tabSales Invoice Item`.base_net_amount - ) as base_net_amount, - sum( - `tabSales Invoice Item`.base_price_list_rate - ) as base_price_list_rate, - + sum(`tabSales Invoice Item`.stock_qty) AS stock_qty, + sum(`tabSales Invoice Item`.base_net_amount) AS base_net_amount, + sum(`tabSales Invoice Item`.base_price_list_rate) AS base_price_list_rate, `tabSales Invoice Item`.parent, `tabSales Invoice Item`.item_code, - `tabGST HSN Code`.description, - json_extract( - `tabSales Taxes and Charges`.item_wise_tax_detail, - concat( - '$."', `tabSales Invoice Item`.item_code, - '"[0]' - ) - ) * count( - distinct `tabSales Taxes and Charges`.name - ) as tax_rate + `tabGST HSN Code`.description FROM `tabSales Invoice` - INNER JOIN - `tabSales Invoice Item` ON `tabSales Invoice`.name = `tabSales Invoice Item`.parent - INNER JOIN - `tabGST HSN Code` ON `tabSales Invoice Item`.gst_hsn_code = `tabGST HSN Code`.name % s % s - LEFT JOIN - `tabSales Taxes and Charges` ON `tabSales Taxes and Charges`.parent = `tabSales Invoice`.name + INNER JOIN `tabSales Invoice Item` ON `tabSales Invoice`.name = `tabSales Invoice Item`.parent + INNER JOIN `tabGST HSN Code` ON `tabSales Invoice Item`.gst_hsn_code = `tabGST HSN Code`.name % s % s WHERE `tabSales Invoice`.docstatus = 1 - AND - `tabSales Invoice Item`.gst_hsn_code is not NULL + AND `tabSales Invoice Item`.gst_hsn_code IS NOT NULL GROUP BY `tabSales Invoice Item`.parent, `tabSales Invoice Item`.item_code, @@ -158,6 +178,7 @@ def get_tax_accounts( item_list, columns, company_currency, + output_gst_accounts, doctype="Sales Invoice", tax_doctype="Sales Taxes and Charges", ): @@ -210,15 +231,25 @@ def get_tax_accounts( continue itemised_tax.setdefault(item_code, frappe._dict()) if isinstance(tax_data, list): + tax_rate = 0 + is_gst_tax = 0 + if account_head in output_gst_accounts: + is_gst_tax = 1 + tax_rate = tax_data[0] tax_amount = tax_data[1] else: + tax_rate = 0 tax_amount = 0 for d in item_row_map.get(parent, {}).get(item_code, []): item_tax_amount = tax_amount if item_tax_amount: itemised_tax.setdefault((parent, item_code), {})[account_head] = frappe._dict( - {"tax_amount": flt(item_tax_amount, tax_amount_precision)} + { + "tax_rate": flt(tax_rate, 2), + "is_gst_tax": is_gst_tax, + "tax_amount": flt(item_tax_amount, tax_amount_precision), + } ) except ValueError: continue From b145fe3b3e7d1e8faaffc29106bbd5784e06e9d1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 31 Jul 2022 19:12:14 +0530 Subject: [PATCH 05/26] fix(pos): validate product bundles while submitting pos invoice (backport #31615) (#31657) --- erpnext/accounts/doctype/pos_invoice/pos_invoice.py | 3 --- erpnext/selling/page/point_of_sale/pos_payment.js | 5 +++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 96975e9d116..59c7c843edf 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -222,9 +222,6 @@ class POSInvoice(SalesInvoice): allow_negative_stock = frappe.db.get_single_value("Stock Settings", "allow_negative_stock") for d in self.get("items"): - is_service_item = not (frappe.db.get_value("Item", d.get("item_code"), "is_stock_item")) - if is_service_item: - return if d.serial_no: self.validate_pos_reserved_serial_nos(d) self.validate_delivered_serial_nos(d) diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index b4ece46e6e1..539a0278e2f 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -161,13 +161,14 @@ erpnext.PointOfSale.Payment = class { frappe.ui.form.on('POS Invoice', 'contact_mobile', (frm) => { const contact = frm.doc.contact_mobile; + if (!this.request_for_payment_field) return; const request_button = $(this.request_for_payment_field.$input[0]); if (contact) { request_button.removeClass('btn-default').addClass('btn-primary'); } else { request_button.removeClass('btn-primary').addClass('btn-default'); - } - }); + } + }); frappe.ui.form.on('POS Invoice', 'coupon_code', (frm) => { if (frm.doc.coupon_code && !frm.applying_pos_coupon_code) { From 26aef4fb1c00129ff9ea2c717110814a6ee108dd Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 1 Aug 2022 12:54:25 +0530 Subject: [PATCH 06/26] fix: statistical component showing up in salary slip (#31746) * fix: statistical component showing up in salary slip * fix(test): payment days effect on timesheet salary slio --- .../doctype/salary_slip/salary_slip.py | 6 ++-- .../doctype/salary_slip/test_salary_slip.py | 33 ++++++++++++++++--- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 4db0d2c366a..07149c25658 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -633,10 +633,8 @@ class SalarySlip(TransactionBase): amount = self.eval_condition_and_formula(struct_row, data) if ( - amount - or (struct_row.amount_based_on_formula and amount is not None) - and struct_row.statistical_component == 0 - ): + amount or (struct_row.amount_based_on_formula and amount is not None) + ) and struct_row.statistical_component == 0: self.update_component_row(struct_row, amount, component_type, data=data) def get_data_for_eval(self): diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index 425a03b3ba6..8604ed453b7 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -372,13 +372,19 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(salary_slip.payment_days, days_in_month - no_of_holidays - 1) - # gross pay calculation based on attendance (payment days) - gross_pay = 78100 - ( - (78000 / (days_in_month - no_of_holidays)) - * flt(salary_slip.leave_without_pay + salary_slip.absent_days) + # component calculation based on attendance (payment days) + amount, precision = None, None + + for row in salary_slip.earnings: + if row.salary_component == "Basic Salary": + amount = row.amount + precision = row.precision("amount") + break + expected_amount = flt( + (50000 * salary_slip.payment_days / salary_slip.total_working_days), precision ) - self.assertEqual(salary_slip.gross_pay, flt(gross_pay, 2)) + self.assertEqual(amount, expected_amount) @change_settings("Payroll Settings", {"payroll_based_on": "Attendance"}) def test_component_amount_dependent_on_another_payment_days_based_component(self): @@ -981,6 +987,16 @@ class TestSalarySlip(unittest.TestCase): frappe.db.rollback() + def test_do_not_show_statistical_component_in_slip(self): + make_employee("test_statistical_component@salary.com") + new_ss = make_employee_salary_slip( + "test_statistical_component@salary.com", + "Monthly", + "Test Payment Based On Attendence", + ) + components = [row.salary_component for row in new_ss.get("earnings")] + self.assertNotIn("Statistical Component", components) + def make_activity_for_employee(self): activity_type = frappe.get_doc("Activity Type", "_Test Activity Type") activity_type.billing_rate = 50 @@ -1122,6 +1138,13 @@ def make_earning_salary_component( "depends_on_payment_days": 0, }, {"salary_component": "Leave Encashment", "abbr": "LE", "type": "Earning"}, + { + "salary_component": "Statistical Component", + "abbr": "SC", + "type": "Earning", + "statistical_component": 1, + "amount": 500, + }, ] if include_flexi_benefits: data.extend( From e9e53a74c910633660525d660d8833eec3e6e095 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 1 Aug 2022 14:03:12 +0530 Subject: [PATCH 07/26] fix: minor changed link (cherry picked from commit 0e7c4314b40b98efc144f2f5491fde2e16c40d46) --- .../doctype/request_for_quotation/request_for_quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index bc32b1d1971..24555961666 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -113,7 +113,7 @@ class RequestforQuotation(BuyingController): def get_link(self): # RFQ link for supplier portal - return get_url("/rfq/" + self.name) + return get_url("/app/request-for-quotation/" + self.name) def update_supplier_part_no(self, supplier): self.vendor = supplier From 473a43b6b12d2b4370e365c68c9ad68a57359ffa Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 11:23:10 +0530 Subject: [PATCH 08/26] =?UTF-8?q?fix:=20getting=20error=20to=20show=20sale?= =?UTF-8?q?s=20invoice=20group=20or=20print=20rep=E2=80=A6=20(backport=20#?= =?UTF-8?q?31756)=20(#31768)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: getting error to show sales invoice group or print rep… (#31756) fix: formatter getting error to show sales invoice group or print report. 1 - When I view the Gross Profit report in Sales Invoice mode, the table is all broken. Error on browser console: TypeError: Cannot read properties of undefined (reading 'indent') 2 - When I try to print, no matter the Group (Sales Invoice, Item Code, Item Group...) nothing happens. in browser log console I have the following error: TypeError: Cannot read properties of undefined (reading 'content') i fixed both errors and all working perfectly. (cherry picked from commit ea88451875e802a47d3e46e4dbcca33c1278662c) Co-authored-by: HarryPaulo --- erpnext/accounts/report/gross_profit/gross_profit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js index 158ff4d3437..4000d3baf29 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.js +++ b/erpnext/accounts/report/gross_profit/gross_profit.js @@ -44,14 +44,14 @@ frappe.query_reports["Gross Profit"] = { "parent_field": "parent_invoice", "initial_depth": 3, "formatter": function(value, row, column, data, default_formatter) { - if (column.fieldname == "sales_invoice" && column.options == "Item" && data.indent == 0) { + if (column.fieldname == "sales_invoice" && column.options == "Item" && data && data.indent == 0) { column._options = "Sales Invoice"; } else { column._options = "Item"; } value = default_formatter(value, row, column, data); - if (data && (data.indent == 0.0 || row[1].content == "Total")) { + if (data && (data.indent == 0.0 || (row[1] && row[1].content == "Total"))) { value = $(`${value}`); var $value = $(value).css("font-weight", "bold"); value = $value.wrap("

").parent().html(); From 0dbfb1589e1aa15a93d2d9ce815a85d639376320 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 12:03:53 +0530 Subject: [PATCH 09/26] fix: specify allowed doctype in queries (#31765) Co-authored-by: Sagar Vora --- erpnext/controllers/queries.py | 42 ++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 6519028c934..53a17422004 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -18,8 +18,9 @@ from erpnext.stock.get_item_details import _get_item_tax_template @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def employee_query(doctype, txt, searchfield, start, page_len, filters): + doctype = "Employee" conditions = [] - fields = get_fields("Employee", ["name", "employee_name"]) + fields = get_fields(doctype, ["name", "employee_name"]) return frappe.db.sql( """select {fields} from `tabEmployee` @@ -49,7 +50,8 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def lead_query(doctype, txt, searchfield, start, page_len, filters): - fields = get_fields("Lead", ["name", "lead_name", "company_name"]) + doctype = "Lead" + fields = get_fields(doctype, ["name", "lead_name", "company_name"]) return frappe.db.sql( """select {fields} from `tabLead` @@ -77,6 +79,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def customer_query(doctype, txt, searchfield, start, page_len, filters): + doctype = "Customer" conditions = [] cust_master_name = frappe.defaults.get_user_default("cust_master_name") @@ -85,9 +88,9 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): else: fields = ["name", "customer_name", "customer_group", "territory"] - fields = get_fields("Customer", fields) + fields = get_fields(doctype, fields) - searchfields = frappe.get_meta("Customer").get_search_fields() + searchfields = frappe.get_meta(doctype).get_search_fields() searchfields = " or ".join(field + " like %(txt)s" for field in searchfields) return frappe.db.sql( @@ -116,6 +119,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def supplier_query(doctype, txt, searchfield, start, page_len, filters): + doctype = "Supplier" supp_master_name = frappe.defaults.get_user_default("supp_master_name") if supp_master_name == "Supplier Name": @@ -123,7 +127,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): else: fields = ["name", "supplier_name", "supplier_group"] - fields = get_fields("Supplier", fields) + fields = get_fields(doctype, fields) return frappe.db.sql( """select {field} from `tabSupplier` @@ -147,6 +151,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def tax_account_query(doctype, txt, searchfield, start, page_len, filters): + doctype = "Account" company_currency = erpnext.get_company_currency(filters.get("company")) def get_accounts(with_account_type_filter): @@ -197,13 +202,14 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): + doctype = "Item" conditions = [] if isinstance(filters, str): filters = json.loads(filters) # Get searchfields from meta and use in Item Link field query - meta = frappe.get_meta("Item", cached=True) + meta = frappe.get_meta(doctype, cached=True) searchfields = meta.get_search_fields() # these are handled separately @@ -257,7 +263,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals filters.pop("supplier", None) description_cond = "" - if frappe.db.count("Item", cache=True) < 50000: + if frappe.db.count(doctype, cache=True) < 50000: # scan description only if items are less than 50000 description_cond = "or tabItem.description LIKE %(txt)s" return frappe.db.sql( @@ -300,8 +306,9 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def bom(doctype, txt, searchfield, start, page_len, filters): + doctype = "BOM" conditions = [] - fields = get_fields("BOM", ["name", "item"]) + fields = get_fields(doctype, ["name", "item"]) return frappe.db.sql( """select {fields} @@ -331,6 +338,7 @@ def bom(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_project_name(doctype, txt, searchfield, start, page_len, filters): + doctype = "Project" cond = "" if filters and filters.get("customer"): cond = """(`tabProject`.customer = %s or @@ -338,9 +346,9 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): frappe.db.escape(filters.get("customer")) ) - fields = get_fields("Project", ["name", "project_name"]) - searchfields = frappe.get_meta("Project").get_search_fields() - searchfields = " or ".join([field + " like %(txt)s" for field in searchfields]) + fields = get_fields(doctype, ["name", "project_name"]) + searchfields = frappe.get_meta(doctype).get_search_fields() + searchfields = " or ".join(["`tabProject`." + field + " like %(txt)s" for field in searchfields]) return frappe.db.sql( """select {fields} from `tabProject` @@ -366,7 +374,8 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict): - fields = get_fields("Delivery Note", ["name", "customer", "posting_date"]) + doctype = "Delivery Note" + fields = get_fields(doctype, ["name", "customer", "posting_date"]) return frappe.db.sql( """ @@ -402,6 +411,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_batch_no(doctype, txt, searchfield, start, page_len, filters): + doctype = "Batch" cond = "" if filters.get("posting_date"): cond = "and (batch.expiry_date is null or batch.expiry_date >= %(posting_date)s)" @@ -420,7 +430,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): if filters.get("is_return"): having_clause = "" - meta = frappe.get_meta("Batch", cached=True) + meta = frappe.get_meta(doctype, cached=True) searchfields = meta.get_search_fields() search_columns = "" @@ -496,6 +506,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_account_list(doctype, txt, searchfield, start, page_len, filters): + doctype = "Account" filter_list = [] if isinstance(filters, dict): @@ -514,7 +525,7 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters): filter_list.append([doctype, searchfield, "like", "%%%s%%" % txt]) return frappe.desk.reportview.execute( - "Account", + doctype, filters=filter_list, fields=["name", "parent_account"], limit_start=start, @@ -553,6 +564,7 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters): if not filters: filters = {} + doctype = "Account" condition = "" if filters.get("company"): condition += "and tabAccount.company = %(company)s" @@ -628,6 +640,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters): if not filters: filters = {} + doctype = "Account" condition = "" if filters.get("company"): condition += "and tabAccount.company = %(company)s" @@ -650,6 +663,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters): @frappe.validate_and_sanitize_search_inputs def warehouse_query(doctype, txt, searchfield, start, page_len, filters): # Should be used when item code is passed in filters. + doctype = "Warehouse" conditions, bin_conditions = [], [] filter_dict = get_doctype_wise_filters(filters) From df716fbd0c224f7a11bb8c2363d8b702d72ff40c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:37:39 +0530 Subject: [PATCH 10/26] ci: fix automated release regex (backport #31770) (#31772) ci: fix automated release regex (#31770) [skip ci] (cherry picked from commit 2defb89962fe96afd89a1d36e29888812b08025e) Co-authored-by: Ankush Menat --- .releaserc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.releaserc b/.releaserc index 8a758ed30a6..1f5bb53d248 100644 --- a/.releaserc +++ b/.releaserc @@ -10,7 +10,7 @@ "@semantic-release/release-notes-generator", [ "@semantic-release/exec", { - "prepareCmd": 'sed -ir "s/[0-9]*\.[0-9]*\.[0-9]*/${nextRelease.version}/" erpnext/__init__.py' + "prepareCmd": 'sed -ir -E "s/\"[0-9]+\.[0-9]+\.[0-9]+\"/\"${nextRelease.version}\"/" erpnext/__init__.py' } ], [ From 0e46b33ee3b82ca2ee880431b8548adaca97cfa8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 17:08:46 +0530 Subject: [PATCH 11/26] fix(ecommerce): remove query to non-existing field (backport #31771) (#31774) fix(ecommerce): remove query to non-existing field (#31771) (cherry picked from commit 17b9bfd2497e6dc4b1179bd1eb9584e526606e2a) Co-authored-by: Ankush Menat --- erpnext/setup/doctype/item_group/item_group.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 769b2d88085..a4e23267cd4 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -143,10 +143,6 @@ def get_item_for_list_in_html(context): if (context.get("website_image") or "").startswith("files/"): context["website_image"] = "/" + quote(context["website_image"]) - context["show_availability_status"] = cint( - frappe.db.get_single_value("E Commerce Settings", "show_availability_status") - ) - products_template = "templates/includes/products_as_list.html" return frappe.get_template(products_template).render(context) From 16c94d292c27d06d6f94ceef4f78b73283e79785 Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Thu, 4 Aug 2022 19:04:34 +0530 Subject: [PATCH 12/26] fix: pending principal- amount (cherry picked from commit a272d73dd9b30d014829d97c9d0a642286d7d38e) # Conflicts: # erpnext/loan_management/doctype/loan_repayment/loan_repayment.json --- .../loan_balance_adjustment.py | 2 +- .../loan_disbursement/loan_disbursement.json | 6 ++--- .../loan_disbursement/loan_disbursement.py | 3 +++ .../loan_interest_accrual.py | 3 +++ .../loan_repayment/loan_repayment.json | 8 ++++-- .../doctype/loan_repayment/loan_repayment.py | 5 +++- .../loan_security_unpledge.py | 3 +++ .../doctype/loan_write_off/loan_write_off.py | 25 ++++++++++++++----- 8 files changed, 42 insertions(+), 13 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py index 0a576d69692..514a5fcfafb 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py @@ -99,7 +99,7 @@ class LoanBalanceAdjustment(AccountsController): loan_account = frappe.db.get_value("Loan", self.loan, "loan_account") remarks = "{} against loan {}".format(self.adjustment_type.capitalize(), self.loan) if self.reference_number: - remarks += "with reference no. {}".format(self.reference_number) + remarks += " with reference no. {}".format(self.reference_number) loan_entry = { "account": loan_account, diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json index 50926d77268..c7b5c033756 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json @@ -163,11 +163,11 @@ }, { "fetch_from": "against_loan.disbursement_account", + "fetch_if_empty": 1, "fieldname": "disbursement_account", "fieldtype": "Link", "label": "Disbursement Account", - "options": "Account", - "read_only": 1 + "options": "Account" }, { "fieldname": "column_break_16", @@ -185,7 +185,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-02-17 18:23:44.157598", + "modified": "2022-08-04 17:16:04.922444", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Disbursement", diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py index 10174e531a1..8a493a5fc77 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -209,6 +209,9 @@ def get_disbursal_amount(loan, on_current_security_price=0): "loan_amount", "disbursed_amount", "total_payment", + "debit_adjustment_amount", + "credit_adjustment_amount", + "refund_amount", "total_principal_paid", "total_interest_payable", "status", diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 3a4c6513e45..ef53303f5a1 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -147,6 +147,9 @@ def make_accrual_interest_entry_for_demand_loans( "name", "total_payment", "total_amount_paid", + "debit_adjustment_amount", + "credit_adjustment_amount", + "refund_amount", "loan_account", "interest_income_account", "loan_amount", diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json index 480e010b49a..cd807984dab 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json @@ -281,11 +281,11 @@ }, { "fetch_from": "against_loan.payment_account", + "fetch_if_empty": 1, "fieldname": "payment_account", "fieldtype": "Link", "label": "Repayment Account", - "options": "Account", - "read_only": 1 + "options": "Account" }, { "fieldname": "column_break_36", @@ -311,7 +311,11 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2022-02-18 19:10:07.742298", +======= + "modified": "2022-08-04 17:13:51.964203", +>>>>>>> a272d73dd9 (fix: pending principal- amount) "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Repayment", diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 18803504eb1..2f9f4571f28 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -150,6 +150,9 @@ class LoanRepayment(AccountsController): "status", "is_secured_loan", "total_payment", + "debit_adjustment_amount", + "credit_adjustment_amount", + "refund_amount", "loan_amount", "disbursed_amount", "total_interest_payable", @@ -399,7 +402,7 @@ class LoanRepayment(AccountsController): remarks = "Repayment against loan " + self.against_loan if self.reference_number: - remarks += "with reference no. {}".format(self.reference_number) + remarks += " with reference no. {}".format(self.reference_number) if self.repay_from_salary: payment_account = self.payroll_payable_account diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py index 731b65e9a29..3f773011b29 100644 --- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py +++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py @@ -58,6 +58,9 @@ class LoanSecurityUnpledge(Document): self.loan, [ "total_payment", + "debit_adjustment_amount", + "credit_adjustment_amount", + "refund_amount", "total_principal_paid", "loan_amount", "total_interest_payable", diff --git a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py index e19fd15fc84..2eeb97c8f89 100644 --- a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py +++ b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py @@ -9,6 +9,9 @@ from frappe.utils import cint, flt, getdate import erpnext from erpnext.accounts.general_ledger import make_gl_entries from erpnext.controllers.accounts_controller import AccountsController +from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( + get_pending_principal_amount, +) class LoanWriteOff(AccountsController): @@ -22,16 +25,26 @@ class LoanWriteOff(AccountsController): def validate_write_off_amount(self): precision = cint(frappe.db.get_default("currency_precision")) or 2 - total_payment, principal_paid, interest_payable, written_off_amount = frappe.get_value( + + loan_details = frappe.get_value( "Loan", self.loan, - ["total_payment", "total_principal_paid", "total_interest_payable", "written_off_amount"], + [ + "total_payment", + "debit_adjustment_amount", + "credit_adjustment_amount", + "refund_amount", + "total_principal_paid", + "loan_amount", + "total_interest_payable", + "written_off_amount", + "disbursed_amount", + "status", + ], + as_dict=1, ) - pending_principal_amount = flt( - flt(total_payment) - flt(interest_payable) - flt(principal_paid) - flt(written_off_amount), - precision, - ) + pending_principal_amount = flt(get_pending_principal_amount(loan_details), precision) if self.write_off_amount > pending_principal_amount: frappe.throw(_("Write off amount cannot be greater than pending principal amount")) From fb3725752ff005aff70493d94df87418ff8b3831 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 8 Aug 2022 11:53:49 +0530 Subject: [PATCH 13/26] chore: resolve conflicts --- .../doctype/loan_repayment/loan_repayment.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json index cd807984dab..71f93e26bdb 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json @@ -311,11 +311,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2022-02-18 19:10:07.742298", -======= "modified": "2022-08-04 17:13:51.964203", ->>>>>>> a272d73dd9 (fix: pending principal- amount) "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Repayment", @@ -357,4 +353,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} From a17acfdaa2f60a873a823ffcddc2b3e7e07711cf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 4 Aug 2022 09:48:48 +0530 Subject: [PATCH 14/26] fix: intercompany SO throws exception (cherry picked from commit af0a353b79ed3b6a079c142be69da9cc75df2929) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index b8f1e314e82..29f3970d0dc 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -2173,13 +2173,13 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): target_detail_field = "sales_invoice_item" if doctype == "Sales Invoice" else "sales_order_item" source_document_warehouse_field = "target_warehouse" target_document_warehouse_field = "from_warehouse" + received_items = get_received_items(source_name, target_doctype, target_detail_field) else: source_doc = frappe.get_doc(doctype, source_name) target_doctype = "Sales Invoice" if doctype == "Purchase Invoice" else "Sales Order" source_document_warehouse_field = "from_warehouse" target_document_warehouse_field = "target_warehouse" - - received_items = get_received_items(source_name, target_doctype, target_detail_field) + received_items = {} validate_inter_company_transaction(source_doc, doctype) details = get_inter_company_details(source_doc, doctype) From 809d5caf8091c9cfca0550b61043be2fef3e3dbc Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 28 Jul 2022 07:11:16 +0530 Subject: [PATCH 15/26] fix: add asset repair to accounting dimension list (cherry picked from commit 452584c4bd512f1eb0e80acbab5fd1dc0982fc9c) --- erpnext/hooks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index cf1714a25e1..0556d40a4b8 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -589,6 +589,7 @@ accounting_dimension_doctypes = [ "Shipping Rule", "Landed Cost Item", "Asset Value Adjustment", + "Asset Repair", "Loyalty Program", "Fee Schedule", "Fee Structure", From a420d0242cdd4927718c269fd8e7aab702151e4f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 4 Aug 2022 16:48:03 +0530 Subject: [PATCH 16/26] chore: patch for creating existing dimensions in asset repair (cherry picked from commit 80f508c4b12ddfead304674a7eea0f8a7cadbc19) # Conflicts: # erpnext/patches.txt --- erpnext/patches.txt | 22 ++++++++++++++ ..._accounting_dimensions_for_asset_repair.py | 29 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 erpnext/patches/v13_0/create_accounting_dimensions_for_asset_repair.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 9077ed662ff..58d1bc00a0b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -344,6 +344,28 @@ erpnext.patches.v13_0.agriculture_deprecation_warning erpnext.patches.v13_0.update_maintenance_schedule_field_in_visit erpnext.patches.v13_0.hospitality_deprecation_warning erpnext.patches.v13_0.delete_bank_reconciliation_detail +<<<<<<< HEAD +======= +erpnext.patches.v13_0.enable_provisional_accounting +erpnext.patches.v13_0.non_profit_deprecation_warning +erpnext.patches.v13_0.enable_ksa_vat_docs #1 +erpnext.patches.v13_0.show_india_localisation_deprecation_warning +erpnext.patches.v13_0.show_hr_payroll_deprecation_warning +erpnext.patches.v13_0.reset_corrupt_defaults +erpnext.patches.v13_0.create_accounting_dimensions_for_asset_repair + +[post_model_sync] +execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings') +erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents +erpnext.patches.v14_0.delete_shopify_doctypes +erpnext.patches.v14_0.delete_healthcare_doctypes +erpnext.patches.v14_0.delete_hub_doctypes +erpnext.patches.v14_0.delete_hospitality_doctypes # 20-01-2022 +erpnext.patches.v14_0.delete_agriculture_doctypes # 15-06-2022 +erpnext.patches.v14_0.delete_education_doctypes +erpnext.patches.v14_0.delete_datev_doctypes +erpnext.patches.v14_0.rearrange_company_fields +>>>>>>> 80f508c4b1 (chore: patch for creating existing dimensions in asset repair) erpnext.patches.v13_0.update_sane_transfer_against erpnext.patches.v13_0.enable_provisional_accounting erpnext.patches.v13_0.update_disbursement_account diff --git a/erpnext/patches/v13_0/create_accounting_dimensions_for_asset_repair.py b/erpnext/patches/v13_0/create_accounting_dimensions_for_asset_repair.py new file mode 100644 index 00000000000..61a5c86386c --- /dev/null +++ b/erpnext/patches/v13_0/create_accounting_dimensions_for_asset_repair.py @@ -0,0 +1,29 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_field + + +def execute(): + accounting_dimensions = frappe.db.get_all( + "Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"] + ) + + if not accounting_dimensions: + return + + for d in accounting_dimensions: + doctype = "Asset Repair" + field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname}) + + if field: + continue + + df = { + "fieldname": d.fieldname, + "label": d.label, + "fieldtype": "Link", + "options": d.document_type, + "insert_after": "accounting_dimensions_section", + } + + create_custom_field(doctype, df, ignore_validate=True) + frappe.clear_cache(doctype=doctype) From 80981d025f63f43e2900f6db4ae6ca58a849040b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 9 Aug 2022 12:40:06 +0530 Subject: [PATCH 17/26] fix: incorrect tax calculation in case of reduced payment days --- .../doctype/salary_slip/salary_slip.py | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 07149c25658..a5c892687c5 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -622,7 +622,7 @@ class SalarySlip(TransactionBase): self.add_tax_components(payroll_period) def add_structure_components(self, component_type): - data = self.get_data_for_eval() + data, default_data = self.get_data_for_eval() timesheet_component = frappe.db.get_value( "Salary Structure", self.salary_structure, "salary_component" ) @@ -635,7 +635,10 @@ class SalarySlip(TransactionBase): if ( amount or (struct_row.amount_based_on_formula and amount is not None) ) and struct_row.statistical_component == 0: - self.update_component_row(struct_row, amount, component_type, data=data) + default_amount = self.eval_condition_and_formula(struct_row, default_data) + self.update_component_row( + struct_row, amount, component_type, data=data, default_amount=default_amount + ) def get_data_for_eval(self): """Returns data for evaluating formula""" @@ -679,11 +682,15 @@ class SalarySlip(TransactionBase): for sc in salary_components: data.setdefault(sc.salary_component_abbr, 0) + # shallow copy of data to store default amounts (without payment days) for tax calculation + default_data = data.copy() + for key in ("earnings", "deductions"): for d in self.get(key): + default_data[d.abbr] = d.default_amount data[d.abbr] = d.amount - return data + return data, default_data def eval_condition_and_formula(self, d, data): try: @@ -789,7 +796,14 @@ class SalarySlip(TransactionBase): self.update_component_row(tax_row, tax_amount, "deductions") def update_component_row( - self, component_data, amount, component_type, additional_salary=None, is_recurring=0, data=None + self, + component_data, + amount, + component_type, + additional_salary=None, + is_recurring=0, + data=None, + default_amount=None, ): component_row = None for d in self.get(component_type): @@ -850,7 +864,7 @@ class SalarySlip(TransactionBase): additional_salary.deduct_full_tax_on_selected_payroll_date ) else: - component_row.default_amount = amount + component_row.default_amount = default_amount or amount component_row.additional_amount = 0 component_row.deduct_full_tax_on_selected_payroll_date = ( component_data.deduct_full_tax_on_selected_payroll_date @@ -1283,7 +1297,7 @@ class SalarySlip(TransactionBase): )[0].total_amount def calculate_tax_by_tax_slab(self, annual_taxable_earning, tax_slab): - data = self.get_data_for_eval() + data, default_data = self.get_data_for_eval() data.update({"annual_taxable_earning": annual_taxable_earning}) tax_amount = 0 for slab in tax_slab.slabs: From 6c574fbf33d6e06a10f8a0154408625e4da855f9 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 9 Aug 2022 14:39:13 +0530 Subject: [PATCH 18/26] fix: taxable_value and gst_account_heads used taxable_value instead of base_net_amount and only appended required GST accounts --- .../hsn_wise_summary_of_outward_supplies.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py index 95b8e715bee..8df1046abf1 100644 --- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py +++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py @@ -46,8 +46,8 @@ def _execute(filters=None): total_tax += flt(item_tax.get("tax_amount", 0)) row += [tax_rate] - row += [d.base_net_amount + total_tax] - row += [d.base_net_amount] + row += [d.taxable_value + total_tax] + row += [d.taxable_value] for tax in tax_columns: item_tax = itemised_tax.get((d.parent, d.item_code), {}).get(tax, {}) row += [item_tax.get("tax_amount", 0)] @@ -145,7 +145,7 @@ def get_items(filters): `tabSales Invoice Item`.gst_hsn_code, `tabSales Invoice Item`.stock_uom, sum(`tabSales Invoice Item`.stock_qty) AS stock_qty, - sum(`tabSales Invoice Item`.base_net_amount) AS base_net_amount, + sum(`tabSales Invoice Item`.taxable_value) AS taxable_value, sum(`tabSales Invoice Item`.base_price_list_rate) AS base_price_list_rate, `tabSales Invoice Item`.parent, `tabSales Invoice Item`.item_code, @@ -218,7 +218,7 @@ def get_tax_accounts( for parent, account_head, item_wise_tax_detail, tax_amount in tax_details: - if account_head not in tax_columns and tax_amount: + if account_head in output_gst_accounts and account_head not in tax_columns and tax_amount: # as description is text editor earlier and markup can break the column convention in reports tax_columns.append(account_head) @@ -256,14 +256,15 @@ def get_tax_accounts( tax_columns.sort() for account_head in tax_columns: - columns.append( - { - "label": account_head, - "fieldname": frappe.scrub(account_head), - "fieldtype": "Float", - "width": 110, - } - ) + if account_head in output_gst_accounts: + columns.append( + { + "label": account_head, + "fieldname": frappe.scrub(account_head), + "fieldtype": "Float", + "width": 110, + } + ) return itemised_tax, tax_columns From 301d199ece6d3285aa995d1febdf2b3c6b17db41 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 9 Aug 2022 14:57:10 +0530 Subject: [PATCH 19/26] test: default amount in slip --- .../doctype/salary_slip/test_salary_slip.py | 46 +++++++++++++++++-- .../salary_structure/test_salary_structure.py | 2 + 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index 8604ed453b7..987c1ac281e 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -7,7 +7,7 @@ import unittest import frappe from frappe.model.document import Document -from frappe.tests.utils import change_settings +from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import ( add_days, add_months, @@ -35,13 +35,12 @@ from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_detail from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip -class TestSalarySlip(unittest.TestCase): +class TestSalarySlip(FrappeTestCase): def setUp(self): setup_test() frappe.flags.pop("via_payroll_entry", None) def tearDown(self): - frappe.db.rollback() frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) frappe.set_user("Administrator") @@ -925,6 +924,41 @@ class TestSalarySlip(unittest.TestCase): # undelete fixture data frappe.db.rollback() + @change_settings( + "Payroll Settings", + { + "payroll_based_on": "Attendance", + "consider_unmarked_attendance_as": "Present", + "include_holidays_in_total_working_days": True, + }, + ) + def test_default_amount(self): + # Special Allowance (SA) uses another component Basic (BS) in it's formula : BD * .5 + # Basic has "Depends on Payment Days" enabled + # Test default amount for SA is based on default amount for BS (irrespective of PD) + # Test amount for SA is based on amount for BS (based on PD) + from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + month_start_date = get_first_day(nowdate()) + joining_date = add_days(month_start_date, 3) + employee = make_employee("test_tax_for_mid_joinee@salary.com", date_of_joining=joining_date) + + salary_structure = make_salary_structure( + "Stucture to test tax", + "Monthly", + test_tax=True, + from_date=joining_date, + employee=employee, + ) + + ss = make_salary_slip(salary_structure.name, employee=employee) + + # default amount for SA (special allowance = BS*0.5) should be based on default amount for basic + self.assertEqual(ss.earnings[2].default_amount, 25000) + self.assertEqual( + ss.earnings[2].amount, flt(ss.earnings[0].amount * 0.5, ss.earnings[0].precision("amount")) + ) + def test_tax_for_recurring_additional_salary(self): frappe.db.sql("""delete from `tabPayroll Period`""") frappe.db.sql("""delete from `tabSalary Component`""") @@ -1054,7 +1088,7 @@ def make_employee_salary_slip(user, payroll_frequency, salary_structure=None, po def make_salary_component(salary_components, test_tax, company_list=None): for salary_component in salary_components: if frappe.db.exists("Salary Component", salary_component["salary_component"]): - continue + frappe.delete_doc("Salary Component", salary_component["salary_component"], force=True) if test_tax: if salary_component["type"] == "Earning": @@ -1442,6 +1476,10 @@ def setup_test(): "Salary Slip", "Attendance", "Additional Salary", + "Employee Tax Exemption Declaration", + "Employee Tax Exemption Proof Submission", + "Employee Benefit Claim", + "Salary Structure Assignment", ]: frappe.db.sql("delete from `tab%s`" % dt) diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py index acc416fca3f..c598904bbe4 100644 --- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py @@ -150,6 +150,7 @@ def make_salary_structure( currency=erpnext.get_default_currency(), payroll_period=None, include_flexi_benefits=False, + base=None, ): if test_tax: frappe.db.sql("""delete from `tabSalary Structure` where name=%s""", (salary_structure)) @@ -200,6 +201,7 @@ def make_salary_structure( company=company, currency=currency, payroll_period=payroll_period, + base=base, ) return salary_structure_doc From 4e8b39ab3f70792e2b52b2fea568b2895b0fe745 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 9 Aug 2022 14:50:20 +0530 Subject: [PATCH 20/26] fix: incorrect incoming rate set for inter transfer purchase receipt (cherry picked from commit ddd24ea8c8c6f2094c71c607196dd4c91ae6c6a5) --- erpnext/controllers/buying_controller.py | 3 ++- erpnext/stock/stock_ledger.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 64e70b1aef5..9c31ebfbb33 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -301,7 +301,8 @@ class BuyingController(StockController, Subcontracting): rate = flt(outgoing_rate * (d.conversion_factor or 1), d.precision("rate")) else: - rate = frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), "rate") + field = "incoming_rate" if self.get("is_internal_supplier") else "rate" + rate = frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), field) if self.is_internal_transfer(): if rate != d.rate: diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 4e64b7b1833..270acb869c5 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -635,6 +635,24 @@ class update_entries_after(object): voucher_detail_no=sle.voucher_detail_no, sle=sle, ) + + elif ( + sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] + and sle.actual_qty > 0 + and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier") + ): + sle_details = frappe.db.get_value( + "Stock Ledger Entry", + { + "voucher_type": sle.voucher_type, + "voucher_no": sle.voucher_no, + "dependant_sle_voucher_detail_no": sle.voucher_detail_no, + }, + ["stock_value_difference", "actual_qty"], + as_dict=1, + ) + + rate = abs(sle_details.stock_value_difference / sle.actual_qty) else: if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"): rate_field = "valuation_rate" From 9e16f4e4127954dd9d34578bf7f8f5b8d3ad4816 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 9 Aug 2022 17:55:02 +0530 Subject: [PATCH 21/26] chore: resolve conflicts --- erpnext/patches.txt | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 58d1bc00a0b..cbc224f99e4 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -344,28 +344,6 @@ erpnext.patches.v13_0.agriculture_deprecation_warning erpnext.patches.v13_0.update_maintenance_schedule_field_in_visit erpnext.patches.v13_0.hospitality_deprecation_warning erpnext.patches.v13_0.delete_bank_reconciliation_detail -<<<<<<< HEAD -======= -erpnext.patches.v13_0.enable_provisional_accounting -erpnext.patches.v13_0.non_profit_deprecation_warning -erpnext.patches.v13_0.enable_ksa_vat_docs #1 -erpnext.patches.v13_0.show_india_localisation_deprecation_warning -erpnext.patches.v13_0.show_hr_payroll_deprecation_warning -erpnext.patches.v13_0.reset_corrupt_defaults -erpnext.patches.v13_0.create_accounting_dimensions_for_asset_repair - -[post_model_sync] -execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings') -erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents -erpnext.patches.v14_0.delete_shopify_doctypes -erpnext.patches.v14_0.delete_healthcare_doctypes -erpnext.patches.v14_0.delete_hub_doctypes -erpnext.patches.v14_0.delete_hospitality_doctypes # 20-01-2022 -erpnext.patches.v14_0.delete_agriculture_doctypes # 15-06-2022 -erpnext.patches.v14_0.delete_education_doctypes -erpnext.patches.v14_0.delete_datev_doctypes -erpnext.patches.v14_0.rearrange_company_fields ->>>>>>> 80f508c4b1 (chore: patch for creating existing dimensions in asset repair) erpnext.patches.v13_0.update_sane_transfer_against erpnext.patches.v13_0.enable_provisional_accounting erpnext.patches.v13_0.update_disbursement_account @@ -394,3 +372,4 @@ erpnext.patches.v13_0.show_india_localisation_deprecation_warning erpnext.patches.v13_0.fix_number_and_frequency_for_monthly_depreciation erpnext.patches.v13_0.reset_corrupt_defaults erpnext.patches.v13_0.show_hr_payroll_deprecation_warning +erpnext.patches.v13_0.create_accounting_dimensions_for_asset_repair From 14e59c86aa92bba4c752e0c10372c2a2ad57733c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 9 Aug 2022 18:11:13 +0530 Subject: [PATCH 22/26] Tds report (backport #31801) (#31808) * fix: TDS Computation Summary Report not loading, too many values to unpack --- .../report/tds_computation_summary/tds_computation_summary.py | 4 ++-- .../report/tds_payable_monthly/tds_payable_monthly.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py index 08d20086823..db3d5d44a0c 100644 --- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py +++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py @@ -14,9 +14,9 @@ def execute(filters=None): filters.naming_series = frappe.db.get_single_value("Buying Settings", "supp_master_name") columns = get_columns(filters) - tds_docs, tds_accounts, tax_category_map = get_tds_docs(filters) + tds_docs, tds_accounts, tax_category_map, journal_entry_party_map = get_tds_docs(filters) - res = get_result(filters, tds_docs, tds_accounts, tax_category_map) + res = get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_party_map) final_result = group_by_supplier_and_category(res) return columns, final_result diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index 16e0ac1de60..f2809a99c55 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -26,7 +26,6 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_ supplier_map = get_supplier_pan_map() tax_rate_map = get_tax_rate_map(filters) gle_map = get_gle_map(tds_docs) - print(journal_entry_party_map) out = [] for name, details in gle_map.items(): From d820757359ff21fb342867d67ce3f3623a2a5a30 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 9 Aug 2022 18:15:41 +0530 Subject: [PATCH 23/26] fix: Payment Entry button is visible even when claim is fully paid --- erpnext/hr/doctype/expense_claim/expense_claim.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index af80b63845e..f44611368ad 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -254,9 +254,11 @@ frappe.ui.form.on("Expense Claim", { }, __("View")); } - if (frm.doc.docstatus===1 && !cint(frm.doc.is_paid) && cint(frm.doc.grand_total) > 0 - && (cint(frm.doc.total_amount_reimbursed) < cint(frm.doc.total_sanctioned_amount)) - && frappe.model.can_create("Payment Entry")) { + if ( + frm.doc.docstatus === 1 + && frm.doc.status !== "Paid" + && frappe.model.can_create("Payment Entry") + ) { frm.add_custom_button(__('Payment'), function() { frm.events.make_payment_entry(frm); }, __('Create')); } From 0057c10a7dc5f873963782273f1f12794f0e0ae5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 9 Aug 2022 18:19:09 +0530 Subject: [PATCH 24/26] fix: update To Date in Employee Work History (#31811) --- erpnext/hr/doctype/employee/employee.py | 16 +++++++++++++++- .../employee_transfer/test_employee_transfer.py | 3 +++ erpnext/hr/utils.py | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 8aaae5956b0..9c55af067f0 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -10,7 +10,7 @@ from frappe.permissions import ( remove_user_permission, set_user_permission_if_allowed, ) -from frappe.utils import add_years, cstr, getdate, today, validate_email_address +from frappe.utils import add_days, add_years, cstr, getdate, today, validate_email_address from frappe.utils.nestedset import NestedSet from erpnext.utilities.transaction_base import delete_events @@ -64,6 +64,8 @@ class Employee(NestedSet): if existing_user_id: remove_user_permission("Employee", self.name, existing_user_id) + self.update_to_date_in_work_history() + def after_rename(self, old, new, merge): self.db_set("employee", new) @@ -166,6 +168,18 @@ class Employee(NestedSet): user.flags.ignore_permissions = True user.add_roles("Expense Approver") + def update_to_date_in_work_history(self): + if not self.internal_work_history: + return + + for idx, row in enumerate(self.internal_work_history): + if not row.from_date or idx == 0: + continue + + self.internal_work_history[idx - 1].to_date = add_days(row.from_date, -1) + + self.internal_work_history[-1].to_date = None + def validate_date(self): if self.date_of_birth and getdate(self.date_of_birth) > getdate(today()): throw(_("Date of Birth cannot be greater than today.")) diff --git a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py index 37a190a1627..c6bd7d23a76 100644 --- a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py +++ b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py @@ -80,12 +80,14 @@ class TestEmployeeTransfer(unittest.TestCase): department = ["Accounts - TC", "Management - TC"] designation = ["Accountant", "Manager"] dt = [getdate("01-10-2021"), getdate()] + to_date = [add_days(dt[1], -1), None] employee = frappe.get_doc("Employee", employee) for data in employee.internal_work_history: self.assertEqual(data.department, department[count]) self.assertEqual(data.designation, designation[count]) self.assertEqual(data.from_date, dt[count]) + self.assertEqual(data.to_date, to_date[count]) count = count + 1 transfer.cancel() @@ -95,6 +97,7 @@ class TestEmployeeTransfer(unittest.TestCase): self.assertEqual(data.designation, designation[0]) self.assertEqual(data.department, department[0]) self.assertEqual(data.from_date, dt[0]) + self.assertEqual(data.to_date, None) def create_company(): diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 9707b81089b..b80226b224c 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -224,6 +224,7 @@ def delete_employee_work_history(details, employee, date): filters["from_date"] = date if filters: frappe.db.delete("Employee Internal Work History", filters) + employee.reload() @frappe.whitelist() From 42b395916dbe35a2bdf2aa2fcdcac7523d19dcef Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 9 Aug 2022 18:22:05 +0530 Subject: [PATCH 25/26] fix: f-string and where clause Used f-string formatting and added conditions to WHERE clause --- .../hsn_wise_summary_of_outward_supplies.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py index 8df1046abf1..68815bf1edf 100644 --- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py +++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py @@ -140,7 +140,7 @@ def get_items(filters): match_conditions = " and {0} ".format(match_conditions) items = frappe.db.sql( - """ + f""" SELECT `tabSales Invoice Item`.gst_hsn_code, `tabSales Invoice Item`.stock_uom, @@ -153,10 +153,11 @@ def get_items(filters): FROM `tabSales Invoice` INNER JOIN `tabSales Invoice Item` ON `tabSales Invoice`.name = `tabSales Invoice Item`.parent - INNER JOIN `tabGST HSN Code` ON `tabSales Invoice Item`.gst_hsn_code = `tabGST HSN Code`.name % s % s + INNER JOIN `tabGST HSN Code` ON `tabSales Invoice Item`.gst_hsn_code = `tabGST HSN Code`.name WHERE `tabSales Invoice`.docstatus = 1 AND `tabSales Invoice Item`.gst_hsn_code IS NOT NULL + {conditions} GROUP BY `tabSales Invoice Item`.parent, `tabSales Invoice Item`.item_code, @@ -165,8 +166,9 @@ def get_items(filters): ORDER BY `tabSales Invoice Item`.gst_hsn_code, `tabSales Invoice Item`.uom - """ - % (conditions, match_conditions), + """.format( + conditions=conditions + ), filters, as_dict=1, ) From 63cd4349a6efeb42eab2afcc4a897d9a202bd8b6 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 9 Aug 2022 18:28:19 +0530 Subject: [PATCH 26/26] fix: consider precision while validating advance amount against sanctioned amount --- erpnext/hr/doctype/expense_claim/expense_claim.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index 0645bd1f937..43bbede6804 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -305,8 +305,9 @@ class ExpenseClaim(AccountsController): if self.total_advance_amount: precision = self.precision("total_advance_amount") - amount_with_taxes = flt(self.total_sanctioned_amount, precision) + flt( - self.total_taxes_and_charges, precision + amount_with_taxes = flt( + (flt(self.total_sanctioned_amount, precision) + flt(self.total_taxes_and_charges, precision)), + precision, ) if flt(self.total_advance_amount, precision) > amount_with_taxes: