From f7ba7361ca6ebfc0876334e3ecdd54da564cf352 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 18 Jan 2024 14:33:10 +0530 Subject: [PATCH 1/6] fix: set unallocated amount after base tax (cherry picked from commit e9bc63aacf20dd83cb7e15494bb8f2f025491991) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 6b3f46d3833..6d9d6920105 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -919,7 +919,7 @@ frappe.ui.form.on('Payment Entry', { if(frm.doc.payment_type == "Receive" && frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions && frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) { - unallocated_amount = (frm.doc.base_received_amount + total_deductions + flt(frm.doc.base_total_taxes_and_charges) + unallocated_amount = (frm.doc.base_received_amount + total_deductions - flt(frm.doc.base_total_taxes_and_charges) - frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate; } else if (frm.doc.payment_type == "Pay" && frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions From 6895b74ecc952f059e4eb05e67863bafd7139ce0 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 18 Jan 2024 15:05:19 +0530 Subject: [PATCH 2/6] fix: linting issue (cherry picked from commit 99b94af49ffd34bccb76b73c2f06187540444a44) --- .../gross_and_net_profit_report/gross_and_net_profit_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py index f0ca405401d..5ccd4f0f16f 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -134,7 +134,7 @@ def get_revenue(data, period_list, include_in_gross=1): def remove_parent_with_no_child(data): data_to_be_removed = False - for parent in data: + for parent in list(data): if "is_group" in parent and parent.get("is_group") == 1: have_child = False for child in data: From 837fff4533cb6bdf49b2debded62f98aafa6ad2c Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Wed, 17 Jan 2024 12:25:03 +0530 Subject: [PATCH 3/6] Revert "fix(minor): financial statements period end date" (cherry picked from commit 73625a2622f29a83c98179c0b5048a29e61ce243) --- erpnext/accounts/report/financial_statements.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 7355c4b8a16..096bb107069 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -8,17 +8,7 @@ import re import frappe from frappe import _ -from frappe.utils import ( - add_days, - add_months, - cint, - cstr, - flt, - formatdate, - get_first_day, - getdate, - today, -) +from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, @@ -53,8 +43,6 @@ def get_period_list( year_start_date = getdate(period_start_date) year_end_date = getdate(period_end_date) - year_end_date = getdate(today()) if year_end_date > getdate(today()) else year_end_date - months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity] period_list = [] From 3bdff18467a575e4378fd625eebf6a2a666031cb Mon Sep 17 00:00:00 2001 From: David Arnold Date: Mon, 8 Jan 2024 22:07:46 +0100 Subject: [PATCH 4/6] fix: use most reliable section reference per report line (cherry picked from commit b5be17c6dfc9ab10e9eb57669f697fbb5576489c) --- .../tds_payable_monthly.py | 106 ++++++++++-------- 1 file changed, 60 insertions(+), 46 deletions(-) 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 e4953bb1815..b96bfbeb817 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -46,12 +46,10 @@ def get_result( out = [] for name, details in gle_map.items(): - tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0 - bill_no, bill_date = "", "" - tax_withholding_category = tax_category_map.get(name) - rate = tax_rate_map.get(tax_withholding_category) - for entry in details: + tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0 + tax_withholding_category, rate = None, None + bill_no, bill_date = "", "" party = entry.party or entry.against posting_date = entry.posting_date voucher_type = entry.voucher_type @@ -61,12 +59,19 @@ def get_result( if party_list: party = party_list[0] - if not tax_withholding_category: - tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category") - rate = tax_rate_map.get(tax_withholding_category) - - if entry.account in tds_accounts: + if entry.account in tds_accounts.keys(): tax_amount += entry.credit - entry.debit + # infer tax withholding category from the account if it's the single account for this category + tax_withholding_category = tds_accounts.get(entry.account) + rate = tax_rate_map.get(tax_withholding_category) + # or else the consolidated value from the voucher document + if not tax_withholding_category: + # or else from the party default + tax_withholding_category = tax_category_map.get(name) + rate = tax_rate_map.get(tax_withholding_category) + if not tax_withholding_category: + tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category") + rate = tax_rate_map.get(tax_withholding_category) if net_total_map.get(name): if voucher_type == "Journal Entry" and tax_amount and rate: @@ -80,41 +85,41 @@ def get_result( else: total_amount += entry.credit - if tax_amount: - if party_map.get(party, {}).get("party_type") == "Supplier": - party_name = "supplier_name" - party_type = "supplier_type" - else: - party_name = "customer_name" - party_type = "customer_type" + if tax_amount: + if party_map.get(party, {}).get("party_type") == "Supplier": + party_name = "supplier_name" + party_type = "supplier_type" + else: + party_name = "customer_name" + party_type = "customer_type" - row = { - "pan" - if frappe.db.has_column(filters.party_type, "pan") - else "tax_id": party_map.get(party, {}).get("pan"), - "party": party_map.get(party, {}).get("name"), - } - - if filters.naming_series == "Naming Series": - row.update({"party_name": party_map.get(party, {}).get(party_name)}) - - row.update( - { - "section_code": tax_withholding_category or "", - "entity_type": party_map.get(party, {}).get(party_type), - "rate": rate, - "total_amount": total_amount, - "grand_total": grand_total, - "base_total": base_total, - "tax_amount": tax_amount, - "transaction_date": posting_date, - "transaction_type": voucher_type, - "ref_no": name, - "supplier_invoice_no": bill_no, - "supplier_invoice_date": bill_date, + row = { + "pan" + if frappe.db.has_column(filters.party_type, "pan") + else "tax_id": party_map.get(party, {}).get("pan"), + "party": party_map.get(party, {}).get("name"), } - ) - out.append(row) + + if filters.naming_series == "Naming Series": + row.update({"party_name": party_map.get(party, {}).get(party_name)}) + + row.update( + { + "section_code": tax_withholding_category or "", + "entity_type": party_map.get(party, {}).get(party_type), + "rate": rate, + "total_amount": total_amount, + "grand_total": grand_total, + "base_total": base_total, + "tax_amount": tax_amount, + "transaction_date": posting_date, + "transaction_type": voucher_type, + "ref_no": name, + "supplier_invoice_no": bill_no, + "supplier_invoice_date": bill_date, + } + ) + out.append(row) out.sort(key=lambda x: x["section_code"]) @@ -282,11 +287,20 @@ def get_tds_docs(filters): journal_entry_party_map = frappe._dict() bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name") - tds_accounts = frappe.get_all( - "Tax Withholding Account", {"company": filters.get("company")}, pluck="account" + _tds_accounts = frappe.get_all( + "Tax Withholding Account", + {"company": filters.get("company")}, + ["account", "parent"], ) + tds_accounts = {} + for tds_acc in _tds_accounts: + # if it turns out not to be the only tax withholding category, then don't include in the map + if tds_accounts.get(tds_acc["account"]): + tds_accounts[tds_acc["account"]] = None + else: + tds_accounts[tds_acc["account"]] = tds_acc["parent"] - tds_docs = get_tds_docs_query(filters, bank_accounts, tds_accounts).run(as_dict=True) + tds_docs = get_tds_docs_query(filters, bank_accounts, list(tds_accounts.keys())).run(as_dict=True) for d in tds_docs: if d.voucher_type == "Purchase Invoice": From a19b41d8c85a0f364d7f3704851b79bd9b88da5a Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 19 Jan 2024 17:08:02 +0530 Subject: [PATCH 5/6] fix: party field in pdf html (cherry picked from commit b2d9380596dd8ca134777148337ef039f10cc2ca) --- .../accounts_receivable/accounts_receivable.html | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html index ed3b9915591..7d8d33c46b4 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html @@ -10,10 +10,8 @@

{%= __(report.report_name) %}

- {% if (filters.customer_name) { %} - {%= filters.customer_name %} - {% } else { %} - {%= filters.customer || filters.supplier %} + {% if (filters.party) { %} + {%= __(filters.party) %} {% } %}

@@ -141,7 +139,7 @@ {%= __("Reference") %} {% } %} {% if(!filters.show_future_payments) { %} - {%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %} + {%= (filters.party) ? __("Remarks"): __("Party") %} {% } %} {%= __("Invoiced Amount") %} {% if(!filters.show_future_payments) { %} @@ -158,7 +156,7 @@ {%= __("Remaining Balance") %} {% } %} {% } else { %} - {%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %} + {%= (filters.party) ? __("Remarks"): __("Party") %} {%= __("Total Invoiced Amount") %} {%= __("Total Paid Amount") %} {%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %} @@ -187,7 +185,7 @@ {% if(!filters.show_future_payments) { %} - {% if(!(filters.customer || filters.supplier)) { %} + {% if(!(filters.party)) { %} {%= data[i]["party"] %} {% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
{%= data[i]["customer_name"] %} @@ -260,7 +258,7 @@ {% if(data[i]["party"]|| " ") { %} {% if(!data[i]["is_total_row"]) { %} - {% if(!(filters.customer || filters.supplier)) { %} + {% if(!(filters.party)) { %} {%= data[i]["party"] %} {% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
{%= data[i]["customer_name"] %} From 55c9cc3f26409faf80298a6e69305db145d6bd01 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 22 Jan 2024 16:43:54 +0530 Subject: [PATCH 6/6] fix: UOM needs to be whole number not being checked in quotations (cherry picked from commit aaf83da3e9a28244c0f57f01a86b98257bdf503b) --- erpnext/selling/doctype/quotation/quotation.py | 3 ++- .../selling/doctype/quotation/test_quotation.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 539960a508f..d3832172545 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -24,7 +24,8 @@ class Quotation(SellingController): def validate(self): super(Quotation, self).validate() self.set_status() - self.validate_uom_is_integer("stock_uom", "qty") + self.validate_uom_is_integer("stock_uom", "stock_qty") + self.validate_uom_is_integer("uom", "qty") self.validate_valid_till() self.validate_shopping_cart_items() self.set_customer_name() diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 5623a12cdda..691c5b03d2d 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -590,6 +590,22 @@ class TestQuotation(FrappeTestCase): quotation.reload() self.assertEqual(quotation.status, "Ordered") + def test_uom_validation(self): + from erpnext.stock.doctype.item.test_item import make_item + + item = "_Test Item FOR UOM Validation" + make_item(item, {"is_stock_item": 1}) + + if not frappe.db.exists("UOM", "lbs"): + frappe.get_doc({"doctype": "UOM", "uom_name": "lbs", "must_be_whole_number": 1}).insert() + else: + frappe.db.set_value("UOM", "lbs", "must_be_whole_number", 1) + + quotation = make_quotation(item_code=item, qty=1, rate=100, do_not_submit=1) + quotation.items[0].uom = "lbs" + quotation.items[0].conversion_factor = 2.23 + self.assertRaises(frappe.ValidationError, quotation.save) + test_records = frappe.get_test_records("Quotation")