diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 9575f33fcf3..9bb14b4cfc2 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -175,52 +175,53 @@ class PaymentEntry(AccountsController): frappe.throw(fail_message.format(d.idx)) def validate_allocated_amount_with_latest_data(self): - latest_references = get_outstanding_reference_documents( - { - "posting_date": self.posting_date, - "company": self.company, - "party_type": self.party_type, - "payment_type": self.payment_type, - "party": self.party, - "party_account": self.paid_from if self.payment_type == "Receive" else self.paid_to, - "get_outstanding_invoices": True, - "get_orders_to_be_billed": True, - } - ) + if self.references: + latest_references = get_outstanding_reference_documents( + { + "posting_date": self.posting_date, + "company": self.company, + "party_type": self.party_type, + "payment_type": self.payment_type, + "party": self.party, + "party_account": self.paid_from if self.payment_type == "Receive" else self.paid_to, + "get_outstanding_invoices": True, + "get_orders_to_be_billed": True, + } + ) - # Group latest_references by (voucher_type, voucher_no) - latest_lookup = {} - for d in latest_references: - d = frappe._dict(d) - latest_lookup.update({(d.voucher_type, d.voucher_no): d}) + # Group latest_references by (voucher_type, voucher_no) + latest_lookup = {} + for d in latest_references: + d = frappe._dict(d) + latest_lookup.update({(d.voucher_type, d.voucher_no): d}) - for d in self.get("references"): - latest = latest_lookup.get((d.reference_doctype, d.reference_name)) + for d in self.get("references"): + latest = latest_lookup.get((d.reference_doctype, d.reference_name)) - # The reference has already been fully paid - if not latest: - frappe.throw( - _("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name) - ) - # The reference has already been partly paid - elif latest.outstanding_amount < latest.invoice_amount and flt( - d.outstanding_amount, d.precision("outstanding_amount") - ) != flt(latest.outstanding_amount, d.precision("outstanding_amount")): + # The reference has already been fully paid + if not latest: + frappe.throw( + _("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name) + ) + # The reference has already been partly paid + elif latest.outstanding_amount < latest.invoice_amount and flt( + d.outstanding_amount, d.precision("outstanding_amount") + ) != flt(latest.outstanding_amount, d.precision("outstanding_amount")): - frappe.throw( - _( - "{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts." - ).format(_(d.reference_doctype), d.reference_name) - ) + frappe.throw( + _( + "{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts." + ).format(_(d.reference_doctype), d.reference_name) + ) - fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.") + fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.") - if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount): - frappe.throw(fail_message.format(d.idx)) + if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount): + frappe.throw(fail_message.format(d.idx)) - # Check for negative outstanding invoices as well - if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(latest.outstanding_amount): - frappe.throw(fail_message.format(d.idx)) + # Check for negative outstanding invoices as well + if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(latest.outstanding_amount): + frappe.throw(fail_message.format(d.idx)) def delink_advance_entry_references(self): for reference in self.references: 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 a6b91806e78..6e468131461 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 @@ -306,7 +306,7 @@ def get_last_accrual_date(loan, posting_date): def get_last_disbursement_date(loan, posting_date): last_disbursement_date = frappe.db.get_value( "Loan Disbursement", - {"docstatus": 1, "against_loan": loan, "posting_date": ("<", posting_date)}, + {"docstatus": 1, "against_loan": loan, "posting_date": ("<=", posting_date)}, "MAX(posting_date)", ) diff --git a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py index a58f40362ba..40aa9acc3c6 100644 --- a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py +++ b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py @@ -3,11 +3,11 @@ import frappe -from frappe import _ +from frappe import _, qb +from frappe.query_builder import Criterion from erpnext import get_default_company from erpnext.accounts.party import get_party_details -from erpnext.stock.get_item_details import get_price_list_rate_for def execute(filters=None): @@ -50,6 +50,42 @@ def get_columns(filters=None): ] +def fetch_item_prices( + customer: str = None, price_list: str = None, selling_price_list: str = None, items: list = None +): + price_list_map = frappe._dict() + ip = qb.DocType("Item Price") + and_conditions = [] + or_conditions = [] + if items: + and_conditions.append(ip.item_code.isin([x.item_code for x in items])) + and_conditions.append(ip.selling == True) + + or_conditions.append(ip.customer == None) + or_conditions.append(ip.price_list == None) + + if customer: + or_conditions.append(ip.customer == customer) + + if price_list: + or_conditions.append(ip.price_list == price_list) + + if selling_price_list: + or_conditions.append(ip.price_list == selling_price_list) + + res = ( + qb.from_(ip) + .select(ip.item_code, ip.price_list, ip.price_list_rate) + .where(Criterion.all(and_conditions)) + .where(Criterion.any(or_conditions)) + .run(as_dict=True) + ) + for x in res: + price_list_map.update({(x.item_code, x.price_list): x.price_list_rate}) + + return price_list_map + + def get_data(filters=None): data = [] customer_details = get_customer_details(filters) @@ -59,9 +95,17 @@ def get_data(filters=None): "Bin", fields=["item_code", "sum(actual_qty) AS available"], group_by="item_code" ) item_stock_map = {item.item_code: item.available for item in item_stock_map} + price_list_map = fetch_item_prices( + customer_details.customer, + customer_details.price_list, + customer_details.selling_price_list, + items, + ) for item in items: - price_list_rate = get_price_list_rate_for(customer_details, item.item_code) or 0.0 + price_list_rate = price_list_map.get( + (item.item_code, customer_details.price_list or customer_details.selling_price_list), 0.0 + ) available_stock = item_stock_map.get(item.item_code) data.append(