mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-23 15:09:20 +00:00
refactor(Payment Entry): reduce indentation (#46864)
This commit is contained in:
@@ -339,16 +339,18 @@ class PaymentEntry(AccountsController):
|
|||||||
reference_names.add(key)
|
reference_names.add(key)
|
||||||
|
|
||||||
def set_bank_account_data(self):
|
def set_bank_account_data(self):
|
||||||
if self.bank_account:
|
if not self.bank_account:
|
||||||
bank_data = get_bank_account_details(self.bank_account)
|
return
|
||||||
|
|
||||||
field = "paid_from" if self.payment_type == "Pay" else "paid_to"
|
bank_data = get_bank_account_details(self.bank_account)
|
||||||
|
|
||||||
self.bank = bank_data.bank
|
field = "paid_from" if self.payment_type == "Pay" else "paid_to"
|
||||||
self.bank_account_no = bank_data.bank_account_no
|
|
||||||
|
|
||||||
if not self.get(field):
|
self.bank = bank_data.bank
|
||||||
self.set(field, bank_data.account)
|
self.bank_account_no = bank_data.bank_account_no
|
||||||
|
|
||||||
|
if not self.get(field):
|
||||||
|
self.set(field, bank_data.account)
|
||||||
|
|
||||||
def validate_payment_type_with_outstanding(self):
|
def validate_payment_type_with_outstanding(self):
|
||||||
total_outstanding = sum(d.allocated_amount for d in self.get("references"))
|
total_outstanding = sum(d.allocated_amount for d in self.get("references"))
|
||||||
@@ -366,15 +368,16 @@ class PaymentEntry(AccountsController):
|
|||||||
|
|
||||||
if self.party_type in ("Customer", "Supplier"):
|
if self.party_type in ("Customer", "Supplier"):
|
||||||
self.validate_allocated_amount_with_latest_data()
|
self.validate_allocated_amount_with_latest_data()
|
||||||
else:
|
return
|
||||||
fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.")
|
|
||||||
for d in self.get("references"):
|
|
||||||
if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(d.outstanding_amount):
|
|
||||||
frappe.throw(fail_message.format(d.idx))
|
|
||||||
|
|
||||||
# Check for negative outstanding invoices as well
|
fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.")
|
||||||
if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(d.outstanding_amount):
|
for d in self.get("references"):
|
||||||
frappe.throw(fail_message.format(d.idx))
|
if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(d.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(d.outstanding_amount):
|
||||||
|
frappe.throw(fail_message.format(d.idx))
|
||||||
|
|
||||||
def validate_allocated_amount_as_per_payment_request(self):
|
def validate_allocated_amount_as_per_payment_request(self):
|
||||||
"""
|
"""
|
||||||
@@ -412,91 +415,89 @@ class PaymentEntry(AccountsController):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def validate_allocated_amount_with_latest_data(self):
|
def validate_allocated_amount_with_latest_data(self):
|
||||||
if self.references:
|
if not self.references:
|
||||||
uniq_vouchers = set([(x.reference_doctype, x.reference_name) for x in self.references])
|
return
|
||||||
vouchers = [frappe._dict({"voucher_type": x[0], "voucher_no": x[1]}) for x in uniq_vouchers]
|
|
||||||
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,
|
|
||||||
"vouchers": vouchers,
|
|
||||||
"book_advance_payments_in_separate_party_account": self.book_advance_payments_in_separate_party_account,
|
|
||||||
},
|
|
||||||
validate=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Group latest_references by (voucher_type, voucher_no)
|
uniq_vouchers = {(x.reference_doctype, x.reference_name) for x in self.references}
|
||||||
latest_lookup = {}
|
vouchers = [frappe._dict({"voucher_type": x[0], "voucher_no": x[1]}) for x in uniq_vouchers]
|
||||||
for d in latest_references:
|
latest_references = get_outstanding_reference_documents(
|
||||||
d = frappe._dict(d)
|
{
|
||||||
latest_lookup.setdefault((d.voucher_type, d.voucher_no), frappe._dict())[d.payment_term] = d
|
"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,
|
||||||
|
"vouchers": vouchers,
|
||||||
|
"book_advance_payments_in_separate_party_account": self.book_advance_payments_in_separate_party_account,
|
||||||
|
},
|
||||||
|
validate=True,
|
||||||
|
)
|
||||||
|
|
||||||
for idx, d in enumerate(self.get("references"), start=1):
|
# Group latest_references by (voucher_type, voucher_no)
|
||||||
latest = latest_lookup.get((d.reference_doctype, d.reference_name)) or frappe._dict()
|
latest_lookup = {}
|
||||||
|
for d in latest_references:
|
||||||
|
d = frappe._dict(d)
|
||||||
|
latest_lookup.setdefault((d.voucher_type, d.voucher_no), frappe._dict())[d.payment_term] = d
|
||||||
|
|
||||||
# If term based allocation is enabled, throw
|
for idx, d in enumerate(self.get("references"), start=1):
|
||||||
if (
|
latest = latest_lookup.get((d.reference_doctype, d.reference_name)) or frappe._dict()
|
||||||
d.payment_term is None or d.payment_term == ""
|
|
||||||
) and self.term_based_allocation_enabled_for_reference(d.reference_doctype, d.reference_name):
|
|
||||||
frappe.throw(
|
|
||||||
_(
|
|
||||||
"{0} has Payment Term based allocation enabled. Select a Payment Term for Row #{1} in Payment References section"
|
|
||||||
).format(frappe.bold(d.reference_name), frappe.bold(idx))
|
|
||||||
)
|
|
||||||
|
|
||||||
# if no payment template is used by invoice and has a custom term(no `payment_term`), then invoice outstanding will be in 'None' key
|
# If term based allocation is enabled, throw
|
||||||
latest = latest.get(d.payment_term) or latest.get(None)
|
if (
|
||||||
# The reference has already been fully paid
|
d.payment_term is None or d.payment_term == ""
|
||||||
if not latest:
|
) and self.term_based_allocation_enabled_for_reference(d.reference_doctype, d.reference_name):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("{0} {1} has already been fully paid.").format(
|
_(
|
||||||
_(d.reference_doctype), d.reference_name
|
"{0} has Payment Term based allocation enabled. Select a Payment Term for Row #{1} in Payment References section"
|
||||||
)
|
).format(frappe.bold(d.reference_name), frappe.bold(idx))
|
||||||
)
|
)
|
||||||
# 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"))
|
|
||||||
and d.payment_term == ""
|
|
||||||
):
|
|
||||||
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.")
|
# if no payment template is used by invoice and has a custom term(no `payment_term`), then invoice outstanding will be in 'None' key
|
||||||
|
latest = latest.get(d.payment_term) or latest.get(None)
|
||||||
|
# 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"))
|
||||||
|
and d.payment_term == ""
|
||||||
|
):
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
|
||||||
if (
|
fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.")
|
||||||
d.payment_term
|
|
||||||
and (
|
|
||||||
(flt(d.allocated_amount)) > 0
|
|
||||||
and latest.payment_term_outstanding
|
|
||||||
and (flt(d.allocated_amount) > flt(latest.payment_term_outstanding))
|
|
||||||
)
|
|
||||||
and self.term_based_allocation_enabled_for_reference(
|
|
||||||
d.reference_doctype, d.reference_name
|
|
||||||
)
|
|
||||||
):
|
|
||||||
frappe.throw(
|
|
||||||
_(
|
|
||||||
"Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3}"
|
|
||||||
).format(d.idx, d.allocated_amount, latest.payment_term_outstanding, d.payment_term)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount):
|
if (
|
||||||
frappe.throw(fail_message.format(d.idx))
|
d.payment_term
|
||||||
|
and (
|
||||||
|
(flt(d.allocated_amount)) > 0
|
||||||
|
and latest.payment_term_outstanding
|
||||||
|
and (flt(d.allocated_amount) > flt(latest.payment_term_outstanding))
|
||||||
|
)
|
||||||
|
and self.term_based_allocation_enabled_for_reference(d.reference_doctype, d.reference_name)
|
||||||
|
):
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3}"
|
||||||
|
).format(d.idx, d.allocated_amount, latest.payment_term_outstanding, d.payment_term)
|
||||||
|
)
|
||||||
|
|
||||||
# Check for negative outstanding invoices as well
|
if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount):
|
||||||
if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(latest.outstanding_amount):
|
frappe.throw(fail_message.format(d.idx))
|
||||||
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):
|
def delink_advance_entry_references(self):
|
||||||
for reference in self.references:
|
for reference in self.references:
|
||||||
@@ -562,51 +563,52 @@ class PaymentEntry(AccountsController):
|
|||||||
reference_exchange_details: dict | None = None,
|
reference_exchange_details: dict | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
if d.allocated_amount:
|
if not d.allocated_amount:
|
||||||
if (
|
continue
|
||||||
update_ref_details_only_for
|
|
||||||
and (d.reference_doctype, d.reference_name) not in update_ref_details_only_for
|
if (
|
||||||
):
|
update_ref_details_only_for
|
||||||
|
and (d.reference_doctype, d.reference_name) not in update_ref_details_only_for
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
ref_details = get_reference_details(
|
||||||
|
d.reference_doctype,
|
||||||
|
d.reference_name,
|
||||||
|
self.party_account_currency,
|
||||||
|
self.party_type,
|
||||||
|
self.party,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Only update exchange rate when the reference is Journal Entry
|
||||||
|
if (
|
||||||
|
reference_exchange_details
|
||||||
|
and d.reference_doctype == reference_exchange_details.reference_doctype
|
||||||
|
and d.reference_name == reference_exchange_details.reference_name
|
||||||
|
):
|
||||||
|
ref_details.update({"exchange_rate": reference_exchange_details.exchange_rate})
|
||||||
|
|
||||||
|
for field, value in ref_details.items():
|
||||||
|
if d.exchange_gain_loss:
|
||||||
|
# for cases where gain/loss is booked into invoice
|
||||||
|
# exchange_gain_loss is calculated from invoice & populated
|
||||||
|
# and row.exchange_rate is already set to payment entry's exchange rate
|
||||||
|
# refer -> `update_reference_in_payment_entry()` in utils.py
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ref_details = get_reference_details(
|
if field == "exchange_rate" or not d.get(field) or force:
|
||||||
d.reference_doctype,
|
if self.get("_action") in ("submit", "cancel"):
|
||||||
d.reference_name,
|
d.db_set(field, value)
|
||||||
self.party_account_currency,
|
else:
|
||||||
self.party_type,
|
d.set(field, value)
|
||||||
self.party,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Only update exchange rate when the reference is Journal Entry
|
|
||||||
if (
|
|
||||||
reference_exchange_details
|
|
||||||
and d.reference_doctype == reference_exchange_details.reference_doctype
|
|
||||||
and d.reference_name == reference_exchange_details.reference_name
|
|
||||||
):
|
|
||||||
ref_details.update({"exchange_rate": reference_exchange_details.exchange_rate})
|
|
||||||
|
|
||||||
for field, value in ref_details.items():
|
|
||||||
if d.exchange_gain_loss:
|
|
||||||
# for cases where gain/loss is booked into invoice
|
|
||||||
# exchange_gain_loss is calculated from invoice & populated
|
|
||||||
# and row.exchange_rate is already set to payment entry's exchange rate
|
|
||||||
# refer -> `update_reference_in_payment_entry()` in utils.py
|
|
||||||
continue
|
|
||||||
|
|
||||||
if field == "exchange_rate" or not d.get(field) or force:
|
|
||||||
if self.get("_action") in ("submit", "cancel"):
|
|
||||||
d.db_set(field, value)
|
|
||||||
else:
|
|
||||||
d.set(field, value)
|
|
||||||
|
|
||||||
def validate_payment_type(self):
|
def validate_payment_type(self):
|
||||||
if self.payment_type not in ("Receive", "Pay", "Internal Transfer"):
|
if self.payment_type not in ("Receive", "Pay", "Internal Transfer"):
|
||||||
frappe.throw(_("Payment Type must be one of Receive, Pay and Internal Transfer"))
|
frappe.throw(_("Payment Type must be one of Receive, Pay and Internal Transfer"))
|
||||||
|
|
||||||
def validate_party_details(self):
|
def validate_party_details(self):
|
||||||
if self.party:
|
if self.party and not frappe.db.exists(self.party_type, self.party):
|
||||||
if not frappe.db.exists(self.party_type, self.party):
|
frappe.throw(_("{0} {1} does not exist").format(_(self.party_type), self.party))
|
||||||
frappe.throw(_("{0} {1} does not exist").format(_(self.party_type), self.party))
|
|
||||||
|
|
||||||
def set_exchange_rate(self, ref_doc=None):
|
def set_exchange_rate(self, ref_doc=None):
|
||||||
self.set_source_exchange_rate(ref_doc)
|
self.set_source_exchange_rate(ref_doc)
|
||||||
@@ -616,12 +618,8 @@ class PaymentEntry(AccountsController):
|
|||||||
if self.paid_from:
|
if self.paid_from:
|
||||||
if self.paid_from_account_currency == self.company_currency:
|
if self.paid_from_account_currency == self.company_currency:
|
||||||
self.source_exchange_rate = 1
|
self.source_exchange_rate = 1
|
||||||
else:
|
elif ref_doc and self.paid_from_account_currency == ref_doc.currency:
|
||||||
if ref_doc:
|
self.source_exchange_rate = ref_doc.get("exchange_rate") or ref_doc.get("conversion_rate")
|
||||||
if self.paid_from_account_currency == ref_doc.currency:
|
|
||||||
self.source_exchange_rate = ref_doc.get("exchange_rate") or ref_doc.get(
|
|
||||||
"conversion_rate"
|
|
||||||
)
|
|
||||||
|
|
||||||
if not self.source_exchange_rate:
|
if not self.source_exchange_rate:
|
||||||
self.source_exchange_rate = get_exchange_rate(
|
self.source_exchange_rate = get_exchange_rate(
|
||||||
@@ -632,9 +630,8 @@ class PaymentEntry(AccountsController):
|
|||||||
if self.paid_from_account_currency == self.paid_to_account_currency:
|
if self.paid_from_account_currency == self.paid_to_account_currency:
|
||||||
self.target_exchange_rate = self.source_exchange_rate
|
self.target_exchange_rate = self.source_exchange_rate
|
||||||
elif self.paid_to and not self.target_exchange_rate:
|
elif self.paid_to and not self.target_exchange_rate:
|
||||||
if ref_doc:
|
if ref_doc and self.paid_to_account_currency == ref_doc.currency:
|
||||||
if self.paid_to_account_currency == ref_doc.currency:
|
self.target_exchange_rate = ref_doc.get("exchange_rate") or ref_doc.get("conversion_rate")
|
||||||
self.target_exchange_rate = ref_doc.get("exchange_rate") or ref_doc.get("conversion_rate")
|
|
||||||
|
|
||||||
if not self.target_exchange_rate:
|
if not self.target_exchange_rate:
|
||||||
self.target_exchange_rate = get_exchange_rate(
|
self.target_exchange_rate = get_exchange_rate(
|
||||||
@@ -665,63 +662,61 @@ class PaymentEntry(AccountsController):
|
|||||||
elif d.reference_name:
|
elif d.reference_name:
|
||||||
if not frappe.db.exists(d.reference_doctype, d.reference_name):
|
if not frappe.db.exists(d.reference_doctype, d.reference_name):
|
||||||
frappe.throw(_("{0} {1} does not exist").format(d.reference_doctype, d.reference_name))
|
frappe.throw(_("{0} {1} does not exist").format(d.reference_doctype, d.reference_name))
|
||||||
else:
|
|
||||||
ref_doc = frappe.get_doc(d.reference_doctype, d.reference_name)
|
|
||||||
|
|
||||||
if d.reference_doctype != "Journal Entry":
|
ref_doc = frappe.get_doc(d.reference_doctype, d.reference_name)
|
||||||
if self.party != ref_doc.get(scrub(self.party_type)):
|
|
||||||
frappe.throw(
|
|
||||||
_("{0} {1} is not associated with {2} {3}").format(
|
|
||||||
_(d.reference_doctype), d.reference_name, _(self.party_type), self.party
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.validate_journal_entry()
|
|
||||||
|
|
||||||
if d.reference_doctype in frappe.get_hooks("invoice_doctypes"):
|
if d.reference_doctype != "Journal Entry":
|
||||||
if self.party_type == "Customer":
|
if self.party != ref_doc.get(scrub(self.party_type)):
|
||||||
ref_party_account = (
|
|
||||||
get_party_account_based_on_invoice_discounting(d.reference_name)
|
|
||||||
or ref_doc.debit_to
|
|
||||||
)
|
|
||||||
elif self.party_type == "Supplier":
|
|
||||||
ref_party_account = ref_doc.credit_to
|
|
||||||
elif self.party_type == "Employee":
|
|
||||||
ref_party_account = ref_doc.payable_account
|
|
||||||
|
|
||||||
if (
|
|
||||||
ref_party_account != self.party_account
|
|
||||||
and not self.book_advance_payments_in_separate_party_account
|
|
||||||
):
|
|
||||||
frappe.throw(
|
|
||||||
_("{0} {1} is associated with {2}, but Party Account is {3}").format(
|
|
||||||
_(d.reference_doctype),
|
|
||||||
d.reference_name,
|
|
||||||
ref_party_account,
|
|
||||||
self.party_account,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if ref_doc.doctype == "Purchase Invoice" and ref_doc.get("on_hold"):
|
|
||||||
frappe.throw(
|
|
||||||
_("{0} {1} is on hold").format(_(d.reference_doctype), d.reference_name),
|
|
||||||
title=_("Invalid Purchase Invoice"),
|
|
||||||
)
|
|
||||||
|
|
||||||
if ref_doc.docstatus != 1:
|
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("{0} {1} must be submitted").format(_(d.reference_doctype), d.reference_name)
|
_("{0} {1} is not associated with {2} {3}").format(
|
||||||
|
_(d.reference_doctype), d.reference_name, _(self.party_type), self.party
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
self.validate_journal_entry()
|
||||||
|
|
||||||
|
if d.reference_doctype in frappe.get_hooks("invoice_doctypes"):
|
||||||
|
if self.party_type == "Customer":
|
||||||
|
ref_party_account = (
|
||||||
|
get_party_account_based_on_invoice_discounting(d.reference_name)
|
||||||
|
or ref_doc.debit_to
|
||||||
|
)
|
||||||
|
elif self.party_type == "Supplier":
|
||||||
|
ref_party_account = ref_doc.credit_to
|
||||||
|
elif self.party_type == "Employee":
|
||||||
|
ref_party_account = ref_doc.payable_account
|
||||||
|
|
||||||
|
if (
|
||||||
|
ref_party_account != self.party_account
|
||||||
|
and not self.book_advance_payments_in_separate_party_account
|
||||||
|
):
|
||||||
|
frappe.throw(
|
||||||
|
_("{0} {1} is associated with {2}, but Party Account is {3}").format(
|
||||||
|
_(d.reference_doctype),
|
||||||
|
d.reference_name,
|
||||||
|
ref_party_account,
|
||||||
|
self.party_account,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if ref_doc.doctype == "Purchase Invoice" and ref_doc.get("on_hold"):
|
||||||
|
frappe.throw(
|
||||||
|
_("{0} {1} is on hold").format(_(d.reference_doctype), d.reference_name),
|
||||||
|
title=_("Invalid Purchase Invoice"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if ref_doc.docstatus != 1:
|
||||||
|
frappe.throw(
|
||||||
|
_("{0} {1} must be submitted").format(_(d.reference_doctype), d.reference_name)
|
||||||
|
)
|
||||||
|
|
||||||
def get_valid_reference_doctypes(self):
|
def get_valid_reference_doctypes(self):
|
||||||
if self.party_type == "Customer":
|
if self.party_type == "Customer":
|
||||||
return ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning", "Payment Entry")
|
return ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning", "Payment Entry")
|
||||||
|
elif self.party_type in ["Shareholder", "Employee"]:
|
||||||
|
return ("Journal Entry",)
|
||||||
elif self.party_type == "Supplier":
|
elif self.party_type == "Supplier":
|
||||||
return ("Purchase Order", "Purchase Invoice", "Journal Entry", "Payment Entry")
|
return ("Purchase Order", "Purchase Invoice", "Journal Entry", "Payment Entry")
|
||||||
elif self.party_type == "Shareholder":
|
|
||||||
return ("Journal Entry",)
|
|
||||||
elif self.party_type == "Employee":
|
|
||||||
return ("Journal Entry",)
|
|
||||||
|
|
||||||
def validate_paid_invoices(self):
|
def validate_paid_invoices(self):
|
||||||
no_oustanding_refs = {}
|
no_oustanding_refs = {}
|
||||||
@@ -787,37 +782,39 @@ class PaymentEntry(AccountsController):
|
|||||||
invoice_paid_amount_map = {}
|
invoice_paid_amount_map = {}
|
||||||
|
|
||||||
for ref in self.get("references"):
|
for ref in self.get("references"):
|
||||||
if ref.payment_term and ref.reference_name:
|
if not ref.payment_term or not ref.reference_name:
|
||||||
key = (ref.payment_term, ref.reference_name, ref.reference_doctype)
|
continue
|
||||||
invoice_payment_amount_map.setdefault(key, 0.0)
|
|
||||||
invoice_payment_amount_map[key] += ref.allocated_amount
|
|
||||||
|
|
||||||
if not invoice_paid_amount_map.get(key):
|
key = (ref.payment_term, ref.reference_name, ref.reference_doctype)
|
||||||
payment_schedule = frappe.get_all(
|
invoice_payment_amount_map.setdefault(key, 0.0)
|
||||||
"Payment Schedule",
|
invoice_payment_amount_map[key] += ref.allocated_amount
|
||||||
filters={"parent": ref.reference_name},
|
|
||||||
fields=[
|
|
||||||
"paid_amount",
|
|
||||||
"payment_amount",
|
|
||||||
"payment_term",
|
|
||||||
"discount",
|
|
||||||
"outstanding",
|
|
||||||
"discount_type",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
for term in payment_schedule:
|
|
||||||
invoice_key = (term.payment_term, ref.reference_name, ref.reference_doctype)
|
|
||||||
invoice_paid_amount_map.setdefault(invoice_key, {})
|
|
||||||
invoice_paid_amount_map[invoice_key]["outstanding"] = term.outstanding
|
|
||||||
if not (term.discount_type and term.discount):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if term.discount_type == "Percentage":
|
if not invoice_paid_amount_map.get(key):
|
||||||
invoice_paid_amount_map[invoice_key]["discounted_amt"] = ref.total_amount * (
|
payment_schedule = frappe.get_all(
|
||||||
term.discount / 100
|
"Payment Schedule",
|
||||||
)
|
filters={"parent": ref.reference_name},
|
||||||
else:
|
fields=[
|
||||||
invoice_paid_amount_map[invoice_key]["discounted_amt"] = term.discount
|
"paid_amount",
|
||||||
|
"payment_amount",
|
||||||
|
"payment_term",
|
||||||
|
"discount",
|
||||||
|
"outstanding",
|
||||||
|
"discount_type",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
for term in payment_schedule:
|
||||||
|
invoice_key = (term.payment_term, ref.reference_name, ref.reference_doctype)
|
||||||
|
invoice_paid_amount_map.setdefault(invoice_key, {})
|
||||||
|
invoice_paid_amount_map[invoice_key]["outstanding"] = term.outstanding
|
||||||
|
if not (term.discount_type and term.discount):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if term.discount_type == "Percentage":
|
||||||
|
invoice_paid_amount_map[invoice_key]["discounted_amt"] = ref.total_amount * (
|
||||||
|
term.discount / 100
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
invoice_paid_amount_map[invoice_key]["discounted_amt"] = term.discount
|
||||||
|
|
||||||
for idx, (key, allocated_amount) in enumerate(invoice_payment_amount_map.items(), 1):
|
for idx, (key, allocated_amount) in enumerate(invoice_payment_amount_map.items(), 1):
|
||||||
if not invoice_paid_amount_map.get(key):
|
if not invoice_paid_amount_map.get(key):
|
||||||
@@ -1064,14 +1061,14 @@ class PaymentEntry(AccountsController):
|
|||||||
applicable_tax = 0
|
applicable_tax = 0
|
||||||
base_applicable_tax = 0
|
base_applicable_tax = 0
|
||||||
for tax in self.get("taxes"):
|
for tax in self.get("taxes"):
|
||||||
if not tax.included_in_paid_amount:
|
if tax.included_in_paid_amount:
|
||||||
amount = -1 * tax.tax_amount if tax.add_deduct_tax == "Deduct" else tax.tax_amount
|
continue
|
||||||
base_amount = (
|
|
||||||
-1 * tax.base_tax_amount if tax.add_deduct_tax == "Deduct" else tax.base_tax_amount
|
|
||||||
)
|
|
||||||
|
|
||||||
applicable_tax += amount
|
amount = -1 * tax.tax_amount if tax.add_deduct_tax == "Deduct" else tax.tax_amount
|
||||||
base_applicable_tax += base_amount
|
base_amount = -1 * tax.base_tax_amount if tax.add_deduct_tax == "Deduct" else tax.base_tax_amount
|
||||||
|
|
||||||
|
applicable_tax += amount
|
||||||
|
base_applicable_tax += base_amount
|
||||||
|
|
||||||
self.paid_amount_after_tax = flt(
|
self.paid_amount_after_tax = flt(
|
||||||
flt(self.paid_amount) + flt(applicable_tax), self.precision("paid_amount_after_tax")
|
flt(self.paid_amount) + flt(applicable_tax), self.precision("paid_amount_after_tax")
|
||||||
@@ -1742,25 +1739,27 @@ class PaymentEntry(AccountsController):
|
|||||||
|
|
||||||
def add_deductions_gl_entries(self, gl_entries):
|
def add_deductions_gl_entries(self, gl_entries):
|
||||||
for d in self.get("deductions"):
|
for d in self.get("deductions"):
|
||||||
if d.amount:
|
if not d.amount:
|
||||||
account_currency = get_account_currency(d.account)
|
continue
|
||||||
if account_currency != self.company_currency:
|
|
||||||
frappe.throw(_("Currency for {0} must be {1}").format(d.account, self.company_currency))
|
|
||||||
|
|
||||||
gl_entries.append(
|
account_currency = get_account_currency(d.account)
|
||||||
self.get_gl_dict(
|
if account_currency != self.company_currency:
|
||||||
{
|
frappe.throw(_("Currency for {0} must be {1}").format(d.account, self.company_currency))
|
||||||
"account": d.account,
|
|
||||||
"account_currency": account_currency,
|
gl_entries.append(
|
||||||
"against": self.party or self.paid_from,
|
self.get_gl_dict(
|
||||||
"debit_in_account_currency": d.amount,
|
{
|
||||||
"debit_in_transaction_currency": d.amount / self.transaction_exchange_rate,
|
"account": d.account,
|
||||||
"debit": d.amount,
|
"account_currency": account_currency,
|
||||||
"cost_center": d.cost_center,
|
"against": self.party or self.paid_from,
|
||||||
},
|
"debit_in_account_currency": d.amount,
|
||||||
item=d,
|
"debit_in_transaction_currency": d.amount / self.transaction_exchange_rate,
|
||||||
)
|
"debit": d.amount,
|
||||||
|
"cost_center": d.cost_center,
|
||||||
|
},
|
||||||
|
item=d,
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def get_party_account_for_taxes(self):
|
def get_party_account_for_taxes(self):
|
||||||
if self.payment_type == "Receive":
|
if self.payment_type == "Receive":
|
||||||
@@ -1777,15 +1776,17 @@ class PaymentEntry(AccountsController):
|
|||||||
return flt(gl_dict.get(field, 0) / (conversion_rate or 1))
|
return flt(gl_dict.get(field, 0) / (conversion_rate or 1))
|
||||||
|
|
||||||
def update_advance_paid(self):
|
def update_advance_paid(self):
|
||||||
if self.payment_type in ("Receive", "Pay") and self.party:
|
if self.payment_type not in ("Receive", "Pay") or not self.party:
|
||||||
advance_payment_doctypes = frappe.get_hooks(
|
return
|
||||||
"advance_payment_receivable_doctypes"
|
|
||||||
) + frappe.get_hooks("advance_payment_payable_doctypes")
|
advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks(
|
||||||
for d in self.get("references"):
|
"advance_payment_payable_doctypes"
|
||||||
if d.allocated_amount and d.reference_doctype in advance_payment_doctypes:
|
)
|
||||||
frappe.get_doc(
|
for d in self.get("references"):
|
||||||
d.reference_doctype, d.reference_name, for_update=True
|
if d.allocated_amount and d.reference_doctype in advance_payment_doctypes:
|
||||||
).set_total_advance_paid()
|
frappe.get_doc(
|
||||||
|
d.reference_doctype, d.reference_name, for_update=True
|
||||||
|
).set_total_advance_paid()
|
||||||
|
|
||||||
def on_recurring(self, reference_doc, auto_repeat_doc):
|
def on_recurring(self, reference_doc, auto_repeat_doc):
|
||||||
self.reference_no = reference_doc.name
|
self.reference_no = reference_doc.name
|
||||||
|
|||||||
Reference in New Issue
Block a user