refactor: update voucher outstanding from payment ledger
Outstanding amount is updated from payment ledger, only for receivable/payable accounts. For remaining account types, update happens from GL Entry.
This commit is contained in:
parent
524c175cf0
commit
7312f22f35
@ -58,16 +58,20 @@ class GLEntry(Document):
|
|||||||
validate_balance_type(self.account, adv_adj)
|
validate_balance_type(self.account, adv_adj)
|
||||||
validate_frozen_account(self.account, adv_adj)
|
validate_frozen_account(self.account, adv_adj)
|
||||||
|
|
||||||
# Update outstanding amt on against voucher
|
if frappe.db.get_value("Account", self.account, "account_type") not in [
|
||||||
if (
|
"Receivable",
|
||||||
self.against_voucher_type in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
|
"Payable",
|
||||||
and self.against_voucher
|
]:
|
||||||
and self.flags.update_outstanding == "Yes"
|
# Update outstanding amt on against voucher
|
||||||
and not frappe.flags.is_reverse_depr_entry
|
if (
|
||||||
):
|
self.against_voucher_type in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
|
||||||
update_outstanding_amt(
|
and self.against_voucher
|
||||||
self.account, self.party_type, self.party, self.against_voucher_type, self.against_voucher
|
and self.flags.update_outstanding == "Yes"
|
||||||
)
|
and not frappe.flags.is_reverse_depr_entry
|
||||||
|
):
|
||||||
|
update_outstanding_amt(
|
||||||
|
self.account, self.party_type, self.party, self.against_voucher_type, self.against_voucher
|
||||||
|
)
|
||||||
|
|
||||||
def check_mandatory(self):
|
def check_mandatory(self):
|
||||||
mandatory = ["account", "voucher_type", "voucher_no", "company"]
|
mandatory = ["account", "voucher_type", "voucher_no", "company"]
|
||||||
|
|||||||
@ -6,6 +6,19 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||||
|
get_checks_for_pl_and_bs_accounts,
|
||||||
|
)
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
|
||||||
|
get_dimension_filter_map,
|
||||||
|
)
|
||||||
|
from erpnext.accounts.doctype.gl_entry.gl_entry import (
|
||||||
|
validate_balance_type,
|
||||||
|
validate_frozen_account,
|
||||||
|
)
|
||||||
|
from erpnext.accounts.utils import update_voucher_outstanding
|
||||||
|
from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
|
||||||
|
|
||||||
|
|
||||||
class PaymentLedgerEntry(Document):
|
class PaymentLedgerEntry(Document):
|
||||||
def validate_account(self):
|
def validate_account(self):
|
||||||
@ -18,5 +31,119 @@ class PaymentLedgerEntry(Document):
|
|||||||
if not valid_account:
|
if not valid_account:
|
||||||
frappe.throw(_("{0} account is not of type {1}").format(self.account, self.account_type))
|
frappe.throw(_("{0} account is not of type {1}").format(self.account, self.account_type))
|
||||||
|
|
||||||
|
def validate_account_details(self):
|
||||||
|
"""Account must be ledger, active and not freezed"""
|
||||||
|
|
||||||
|
ret = frappe.db.sql(
|
||||||
|
"""select is_group, docstatus, company
|
||||||
|
from tabAccount where name=%s""",
|
||||||
|
self.account,
|
||||||
|
as_dict=1,
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
if ret.is_group == 1:
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"""{0} {1}: Account {2} is a Group Account and group accounts cannot be used in transactions"""
|
||||||
|
).format(self.voucher_type, self.voucher_no, self.account)
|
||||||
|
)
|
||||||
|
|
||||||
|
if ret.docstatus == 2:
|
||||||
|
frappe.throw(
|
||||||
|
_("{0} {1}: Account {2} is inactive").format(self.voucher_type, self.voucher_no, self.account)
|
||||||
|
)
|
||||||
|
|
||||||
|
if ret.company != self.company:
|
||||||
|
frappe.throw(
|
||||||
|
_("{0} {1}: Account {2} does not belong to Company {3}").format(
|
||||||
|
self.voucher_type, self.voucher_no, self.account, self.company
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate_allowed_dimensions(self):
|
||||||
|
dimension_filter_map = get_dimension_filter_map()
|
||||||
|
for key, value in dimension_filter_map.items():
|
||||||
|
dimension = key[0]
|
||||||
|
account = key[1]
|
||||||
|
|
||||||
|
if self.account == account:
|
||||||
|
if value["is_mandatory"] and not self.get(dimension):
|
||||||
|
frappe.throw(
|
||||||
|
_("{0} is mandatory for account {1}").format(
|
||||||
|
frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)
|
||||||
|
),
|
||||||
|
MandatoryAccountDimensionError,
|
||||||
|
)
|
||||||
|
|
||||||
|
if value["allow_or_restrict"] == "Allow":
|
||||||
|
if self.get(dimension) and self.get(dimension) not in value["allowed_dimensions"]:
|
||||||
|
frappe.throw(
|
||||||
|
_("Invalid value {0} for {1} against account {2}").format(
|
||||||
|
frappe.bold(self.get(dimension)),
|
||||||
|
frappe.bold(frappe.unscrub(dimension)),
|
||||||
|
frappe.bold(self.account),
|
||||||
|
),
|
||||||
|
InvalidAccountDimensionError,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if self.get(dimension) and self.get(dimension) in value["allowed_dimensions"]:
|
||||||
|
frappe.throw(
|
||||||
|
_("Invalid value {0} for {1} against account {2}").format(
|
||||||
|
frappe.bold(self.get(dimension)),
|
||||||
|
frappe.bold(frappe.unscrub(dimension)),
|
||||||
|
frappe.bold(self.account),
|
||||||
|
),
|
||||||
|
InvalidAccountDimensionError,
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate_dimensions_for_pl_and_bs(self):
|
||||||
|
account_type = frappe.db.get_value("Account", self.account, "report_type")
|
||||||
|
|
||||||
|
for dimension in get_checks_for_pl_and_bs_accounts():
|
||||||
|
if (
|
||||||
|
account_type == "Profit and Loss"
|
||||||
|
and self.company == dimension.company
|
||||||
|
and dimension.mandatory_for_pl
|
||||||
|
and not dimension.disabled
|
||||||
|
):
|
||||||
|
if not self.get(dimension.fieldname):
|
||||||
|
frappe.throw(
|
||||||
|
_("Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.").format(
|
||||||
|
dimension.label, self.account
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
account_type == "Balance Sheet"
|
||||||
|
and self.company == dimension.company
|
||||||
|
and dimension.mandatory_for_bs
|
||||||
|
and not dimension.disabled
|
||||||
|
):
|
||||||
|
if not self.get(dimension.fieldname):
|
||||||
|
frappe.throw(
|
||||||
|
_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.").format(
|
||||||
|
dimension.label, self.account
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_account()
|
self.validate_account()
|
||||||
|
|
||||||
|
def on_update(self):
|
||||||
|
adv_adj = self.flags.adv_adj
|
||||||
|
if not self.flags.from_repost:
|
||||||
|
self.validate_account_details()
|
||||||
|
self.validate_dimensions_for_pl_and_bs()
|
||||||
|
self.validate_allowed_dimensions()
|
||||||
|
validate_balance_type(self.account, adv_adj)
|
||||||
|
validate_frozen_account(self.account, adv_adj)
|
||||||
|
|
||||||
|
# update outstanding amount
|
||||||
|
if (
|
||||||
|
self.against_voucher_type in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
|
||||||
|
and self.flags.update_outstanding == "Yes"
|
||||||
|
and not frappe.flags.is_reverse_depr_entry
|
||||||
|
):
|
||||||
|
update_voucher_outstanding(
|
||||||
|
self.against_voucher_type, self.against_voucher_no, self.account, self.party_type, self.party
|
||||||
|
)
|
||||||
|
|||||||
@ -35,7 +35,13 @@ def make_gl_entries(
|
|||||||
validate_disabled_accounts(gl_map)
|
validate_disabled_accounts(gl_map)
|
||||||
gl_map = process_gl_map(gl_map, merge_entries)
|
gl_map = process_gl_map(gl_map, merge_entries)
|
||||||
if gl_map and len(gl_map) > 1:
|
if gl_map and len(gl_map) > 1:
|
||||||
create_payment_ledger_entry(gl_map)
|
create_payment_ledger_entry(
|
||||||
|
gl_map,
|
||||||
|
cancel=0,
|
||||||
|
adv_adj=adv_adj,
|
||||||
|
update_outstanding=update_outstanding,
|
||||||
|
from_repost=from_repost,
|
||||||
|
)
|
||||||
save_entries(gl_map, adv_adj, update_outstanding, from_repost)
|
save_entries(gl_map, adv_adj, update_outstanding, from_repost)
|
||||||
# Post GL Map proccess there may no be any GL Entries
|
# Post GL Map proccess there may no be any GL Entries
|
||||||
elif gl_map:
|
elif gl_map:
|
||||||
@ -482,6 +488,9 @@ def make_reverse_gl_entries(
|
|||||||
|
|
||||||
if gl_entries:
|
if gl_entries:
|
||||||
create_payment_ledger_entry(gl_entries, cancel=1)
|
create_payment_ledger_entry(gl_entries, cancel=1)
|
||||||
|
create_payment_ledger_entry(
|
||||||
|
gl_entries, cancel=1, adv_adj=adv_adj, update_outstanding=update_outstanding
|
||||||
|
)
|
||||||
validate_accounting_period(gl_entries)
|
validate_accounting_period(gl_entries)
|
||||||
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
|
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
|
||||||
set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
|
set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
|
||||||
|
|||||||
@ -1433,6 +1433,36 @@ def create_payment_ledger_entry(
|
|||||||
ple.submit()
|
ple.submit()
|
||||||
|
|
||||||
|
|
||||||
|
def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, party):
|
||||||
|
ple = frappe.qb.DocType("Payment Ledger Entry")
|
||||||
|
vouchers = [frappe._dict({"voucher_type": voucher_type, "voucher_no": voucher_no})]
|
||||||
|
common_filter = []
|
||||||
|
if account:
|
||||||
|
common_filter.append(ple.account == account)
|
||||||
|
|
||||||
|
if party_type:
|
||||||
|
common_filter.append(ple.party_type == party_type)
|
||||||
|
|
||||||
|
if party:
|
||||||
|
common_filter.append(ple.party == party)
|
||||||
|
|
||||||
|
ple_query = QueryPaymentLedger()
|
||||||
|
|
||||||
|
# on cancellation outstanding can be an empty list
|
||||||
|
voucher_outstanding = ple_query.get_voucher_outstandings(vouchers, common_filter=common_filter)
|
||||||
|
if voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"] and voucher_outstanding:
|
||||||
|
outstanding = voucher_outstanding[0]
|
||||||
|
ref_doc = frappe.get_doc(voucher_type, voucher_no)
|
||||||
|
|
||||||
|
# Didn't use db_set for optimisation purpose
|
||||||
|
ref_doc.outstanding_amount = outstanding["outstanding_in_account_currency"]
|
||||||
|
frappe.db.set_value(
|
||||||
|
voucher_type, voucher_no, "outstanding_amount", outstanding["outstanding_in_account_currency"]
|
||||||
|
)
|
||||||
|
|
||||||
|
ref_doc.set_status(update=True)
|
||||||
|
|
||||||
|
|
||||||
def delink_original_entry(pl_entry):
|
def delink_original_entry(pl_entry):
|
||||||
if pl_entry:
|
if pl_entry:
|
||||||
ple = qb.DocType("Payment Ledger Entry")
|
ple = qb.DocType("Payment Ledger Entry")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user