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
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 @@
@@ -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"] %}
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 = []
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:
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":
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")
|