mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-13 02:01:21 +00:00
Merge pull request #39506 from frappe/version-14-hotfix
chore: release v14
This commit is contained in:
@@ -919,7 +919,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
if(frm.doc.payment_type == "Receive"
|
if(frm.doc.payment_type == "Receive"
|
||||||
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
|
&& 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)) {
|
&& 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;
|
- frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
|
||||||
} else if (frm.doc.payment_type == "Pay"
|
} else if (frm.doc.payment_type == "Pay"
|
||||||
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
|
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
|
||||||
|
|||||||
@@ -10,10 +10,8 @@
|
|||||||
|
|
||||||
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
|
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
|
||||||
<h4 class="text-center">
|
<h4 class="text-center">
|
||||||
{% if (filters.customer_name) { %}
|
{% if (filters.party) { %}
|
||||||
{%= filters.customer_name %}
|
{%= __(filters.party) %}
|
||||||
{% } else { %}
|
|
||||||
{%= filters.customer || filters.supplier %}
|
|
||||||
{% } %}
|
{% } %}
|
||||||
</h4>
|
</h4>
|
||||||
<h6 class="text-center">
|
<h6 class="text-center">
|
||||||
@@ -141,7 +139,7 @@
|
|||||||
<th style="width: 24%">{%= __("Reference") %}</th>
|
<th style="width: 24%">{%= __("Reference") %}</th>
|
||||||
{% } %}
|
{% } %}
|
||||||
{% if(!filters.show_future_payments) { %}
|
{% if(!filters.show_future_payments) { %}
|
||||||
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
<th style="width: 20%">{%= (filters.party) ? __("Remarks"): __("Party") %}</th>
|
||||||
{% } %}
|
{% } %}
|
||||||
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
|
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
|
||||||
{% if(!filters.show_future_payments) { %}
|
{% if(!filters.show_future_payments) { %}
|
||||||
@@ -158,7 +156,7 @@
|
|||||||
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
|
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
|
||||||
{% } %}
|
{% } %}
|
||||||
{% } else { %}
|
{% } else { %}
|
||||||
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
<th style="width: 40%">{%= (filters.party) ? __("Remarks"): __("Party") %}</th>
|
||||||
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
|
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
|
||||||
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
|
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
|
||||||
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
|
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
|
||||||
@@ -187,7 +185,7 @@
|
|||||||
|
|
||||||
{% if(!filters.show_future_payments) { %}
|
{% if(!filters.show_future_payments) { %}
|
||||||
<td>
|
<td>
|
||||||
{% if(!(filters.customer || filters.supplier)) { %}
|
{% if(!(filters.party)) { %}
|
||||||
{%= data[i]["party"] %}
|
{%= data[i]["party"] %}
|
||||||
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
|
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
|
||||||
<br> {%= data[i]["customer_name"] %}
|
<br> {%= data[i]["customer_name"] %}
|
||||||
@@ -260,7 +258,7 @@
|
|||||||
{% if(data[i]["party"]|| " ") { %}
|
{% if(data[i]["party"]|| " ") { %}
|
||||||
{% if(!data[i]["is_total_row"]) { %}
|
{% if(!data[i]["is_total_row"]) { %}
|
||||||
<td>
|
<td>
|
||||||
{% if(!(filters.customer || filters.supplier)) { %}
|
{% if(!(filters.party)) { %}
|
||||||
{%= data[i]["party"] %}
|
{%= data[i]["party"] %}
|
||||||
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
|
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
|
||||||
<br> {%= data[i]["customer_name"] %}
|
<br> {%= data[i]["customer_name"] %}
|
||||||
|
|||||||
@@ -8,17 +8,7 @@ import re
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import (
|
from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate
|
||||||
add_days,
|
|
||||||
add_months,
|
|
||||||
cint,
|
|
||||||
cstr,
|
|
||||||
flt,
|
|
||||||
formatdate,
|
|
||||||
get_first_day,
|
|
||||||
getdate,
|
|
||||||
today,
|
|
||||||
)
|
|
||||||
|
|
||||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||||
get_accounting_dimensions,
|
get_accounting_dimensions,
|
||||||
@@ -53,8 +43,6 @@ def get_period_list(
|
|||||||
year_start_date = getdate(period_start_date)
|
year_start_date = getdate(period_start_date)
|
||||||
year_end_date = getdate(period_end_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]
|
months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity]
|
||||||
|
|
||||||
period_list = []
|
period_list = []
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ def get_revenue(data, period_list, include_in_gross=1):
|
|||||||
|
|
||||||
def remove_parent_with_no_child(data):
|
def remove_parent_with_no_child(data):
|
||||||
data_to_be_removed = False
|
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:
|
if "is_group" in parent and parent.get("is_group") == 1:
|
||||||
have_child = False
|
have_child = False
|
||||||
for child in data:
|
for child in data:
|
||||||
|
|||||||
@@ -46,12 +46,10 @@ def get_result(
|
|||||||
|
|
||||||
out = []
|
out = []
|
||||||
for name, details in gle_map.items():
|
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:
|
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
|
party = entry.party or entry.against
|
||||||
posting_date = entry.posting_date
|
posting_date = entry.posting_date
|
||||||
voucher_type = entry.voucher_type
|
voucher_type = entry.voucher_type
|
||||||
@@ -61,12 +59,19 @@ def get_result(
|
|||||||
if party_list:
|
if party_list:
|
||||||
party = party_list[0]
|
party = party_list[0]
|
||||||
|
|
||||||
if not tax_withholding_category:
|
if entry.account in tds_accounts.keys():
|
||||||
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:
|
|
||||||
tax_amount += entry.credit - entry.debit
|
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 net_total_map.get(name):
|
||||||
if voucher_type == "Journal Entry" and tax_amount and rate:
|
if voucher_type == "Journal Entry" and tax_amount and rate:
|
||||||
@@ -80,41 +85,41 @@ def get_result(
|
|||||||
else:
|
else:
|
||||||
total_amount += entry.credit
|
total_amount += entry.credit
|
||||||
|
|
||||||
if tax_amount:
|
if tax_amount:
|
||||||
if party_map.get(party, {}).get("party_type") == "Supplier":
|
if party_map.get(party, {}).get("party_type") == "Supplier":
|
||||||
party_name = "supplier_name"
|
party_name = "supplier_name"
|
||||||
party_type = "supplier_type"
|
party_type = "supplier_type"
|
||||||
else:
|
else:
|
||||||
party_name = "customer_name"
|
party_name = "customer_name"
|
||||||
party_type = "customer_type"
|
party_type = "customer_type"
|
||||||
|
|
||||||
row = {
|
row = {
|
||||||
"pan"
|
"pan"
|
||||||
if frappe.db.has_column(filters.party_type, "pan")
|
if frappe.db.has_column(filters.party_type, "pan")
|
||||||
else "tax_id": party_map.get(party, {}).get("pan"),
|
else "tax_id": party_map.get(party, {}).get("pan"),
|
||||||
"party": party_map.get(party, {}).get("name"),
|
"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,
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
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"])
|
out.sort(key=lambda x: x["section_code"])
|
||||||
|
|
||||||
@@ -282,11 +287,20 @@ def get_tds_docs(filters):
|
|||||||
journal_entry_party_map = frappe._dict()
|
journal_entry_party_map = frappe._dict()
|
||||||
bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name")
|
bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name")
|
||||||
|
|
||||||
tds_accounts = frappe.get_all(
|
_tds_accounts = frappe.get_all(
|
||||||
"Tax Withholding Account", {"company": filters.get("company")}, pluck="account"
|
"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:
|
for d in tds_docs:
|
||||||
if d.voucher_type == "Purchase Invoice":
|
if d.voucher_type == "Purchase Invoice":
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ class Quotation(SellingController):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
super(Quotation, self).validate()
|
super(Quotation, self).validate()
|
||||||
self.set_status()
|
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_valid_till()
|
||||||
self.validate_shopping_cart_items()
|
self.validate_shopping_cart_items()
|
||||||
self.set_customer_name()
|
self.set_customer_name()
|
||||||
|
|||||||
@@ -590,6 +590,22 @@ class TestQuotation(FrappeTestCase):
|
|||||||
quotation.reload()
|
quotation.reload()
|
||||||
self.assertEqual(quotation.status, "Ordered")
|
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")
|
test_records = frappe.get_test_records("Quotation")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user