refactor: payment reconciliation tool (#27128)
This commit is contained in:
@@ -341,31 +341,42 @@ def add_cc(args=None):
|
||||
|
||||
def reconcile_against_document(args):
|
||||
"""
|
||||
Cancel JV, Update aginst document, split if required and resubmit jv
|
||||
Cancel PE or JV, Update against document, split if required and resubmit
|
||||
"""
|
||||
for d in args:
|
||||
# To optimize making GL Entry for PE or JV with multiple references
|
||||
reconciled_entries = {}
|
||||
for row in args:
|
||||
if not reconciled_entries.get((row.voucher_type, row.voucher_no)):
|
||||
reconciled_entries[(row.voucher_type, row.voucher_no)] = []
|
||||
|
||||
check_if_advance_entry_modified(d)
|
||||
validate_allocated_amount(d)
|
||||
reconciled_entries[(row.voucher_type, row.voucher_no)].append(row)
|
||||
|
||||
for key, entries in reconciled_entries.items():
|
||||
voucher_type = key[0]
|
||||
voucher_no = key[1]
|
||||
|
||||
# cancel advance entry
|
||||
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
||||
|
||||
doc = frappe.get_doc(voucher_type, voucher_no)
|
||||
frappe.flags.ignore_party_validation = True
|
||||
doc.make_gl_entries(cancel=1, adv_adj=1)
|
||||
|
||||
# update ref in advance entry
|
||||
if d.voucher_type == "Journal Entry":
|
||||
update_reference_in_journal_entry(d, doc)
|
||||
else:
|
||||
update_reference_in_payment_entry(d, doc)
|
||||
for entry in entries:
|
||||
check_if_advance_entry_modified(entry)
|
||||
validate_allocated_amount(entry)
|
||||
|
||||
# update ref in advance entry
|
||||
if voucher_type == "Journal Entry":
|
||||
update_reference_in_journal_entry(entry, doc, do_not_save=True)
|
||||
else:
|
||||
update_reference_in_payment_entry(entry, doc, do_not_save=True)
|
||||
|
||||
doc.save(ignore_permissions=True)
|
||||
# re-submit advance entry
|
||||
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
||||
doc = frappe.get_doc(entry.voucher_type, entry.voucher_no)
|
||||
doc.make_gl_entries(cancel = 0, adv_adj =1)
|
||||
frappe.flags.ignore_party_validation = False
|
||||
|
||||
if d.voucher_type in ('Payment Entry', 'Journal Entry'):
|
||||
if entry.voucher_type in ('Payment Entry', 'Journal Entry'):
|
||||
doc.update_expense_claim()
|
||||
|
||||
def check_if_advance_entry_modified(args):
|
||||
@@ -374,6 +385,9 @@ def check_if_advance_entry_modified(args):
|
||||
check if amount is same
|
||||
check if jv is submitted
|
||||
"""
|
||||
if not args.get('unreconciled_amount'):
|
||||
args.update({'unreconciled_amount': args.get('unadjusted_amount')})
|
||||
|
||||
ret = None
|
||||
if args.voucher_type == "Journal Entry":
|
||||
ret = frappe.db.sql("""
|
||||
@@ -395,14 +409,14 @@ def check_if_advance_entry_modified(args):
|
||||
and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
|
||||
and t1.party_type = %(party_type)s and t1.party = %(party)s and t1.{0} = %(account)s
|
||||
and t2.reference_doctype in ("", "Sales Order", "Purchase Order")
|
||||
and t2.allocated_amount = %(unadjusted_amount)s
|
||||
and t2.allocated_amount = %(unreconciled_amount)s
|
||||
""".format(party_account_field), args)
|
||||
else:
|
||||
ret = frappe.db.sql("""select name from `tabPayment Entry`
|
||||
where
|
||||
name = %(voucher_no)s and docstatus = 1
|
||||
and party_type = %(party_type)s and party = %(party)s and {0} = %(account)s
|
||||
and unallocated_amount = %(unadjusted_amount)s
|
||||
and unallocated_amount = %(unreconciled_amount)s
|
||||
""".format(party_account_field), args)
|
||||
|
||||
if not ret:
|
||||
@@ -415,58 +429,44 @@ def validate_allocated_amount(args):
|
||||
elif flt(args.get("allocated_amount"), precision) > flt(args.get("unadjusted_amount"), precision):
|
||||
throw(_("Allocated amount cannot be greater than unadjusted amount"))
|
||||
|
||||
def update_reference_in_journal_entry(d, jv_obj):
|
||||
def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
|
||||
"""
|
||||
Updates against document, if partial amount splits into rows
|
||||
"""
|
||||
jv_detail = jv_obj.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
||||
jv_detail.set(d["dr_or_cr"], d["allocated_amount"])
|
||||
jv_detail.set('debit' if d['dr_or_cr']=='debit_in_account_currency' else 'credit',
|
||||
d["allocated_amount"]*flt(jv_detail.exchange_rate))
|
||||
|
||||
original_reference_type = jv_detail.reference_type
|
||||
original_reference_name = jv_detail.reference_name
|
||||
|
||||
jv_detail.set("reference_type", d["against_voucher_type"])
|
||||
jv_detail.set("reference_name", d["against_voucher"])
|
||||
|
||||
if d['allocated_amount'] < d['unadjusted_amount']:
|
||||
jvd = frappe.db.sql("""
|
||||
select cost_center, balance, against_account, is_advance,
|
||||
account_type, exchange_rate, account_currency
|
||||
from `tabJournal Entry Account` where name = %s
|
||||
""", d['voucher_detail_no'], as_dict=True)
|
||||
jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
||||
|
||||
if flt(d['unadjusted_amount']) - flt(d['allocated_amount']) != 0:
|
||||
# adjust the unreconciled balance
|
||||
amount_in_account_currency = flt(d['unadjusted_amount']) - flt(d['allocated_amount'])
|
||||
amount_in_company_currency = amount_in_account_currency * flt(jvd[0]['exchange_rate'])
|
||||
amount_in_company_currency = amount_in_account_currency * flt(jv_detail.exchange_rate)
|
||||
jv_detail.set(d['dr_or_cr'], amount_in_account_currency)
|
||||
jv_detail.set('debit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'credit', amount_in_company_currency)
|
||||
else:
|
||||
journal_entry.remove(jv_detail)
|
||||
|
||||
# new entry with balance amount
|
||||
ch = jv_obj.append("accounts")
|
||||
ch.account = d['account']
|
||||
ch.account_type = jvd[0]['account_type']
|
||||
ch.account_currency = jvd[0]['account_currency']
|
||||
ch.exchange_rate = jvd[0]['exchange_rate']
|
||||
ch.party_type = d["party_type"]
|
||||
ch.party = d["party"]
|
||||
ch.cost_center = cstr(jvd[0]["cost_center"])
|
||||
ch.balance = flt(jvd[0]["balance"])
|
||||
# new row with references
|
||||
new_row = journal_entry.append("accounts")
|
||||
new_row.update(jv_detail.as_dict().copy())
|
||||
|
||||
ch.set(d['dr_or_cr'], amount_in_account_currency)
|
||||
ch.set('debit' if d['dr_or_cr']=='debit_in_account_currency' else 'credit', amount_in_company_currency)
|
||||
new_row.set(d["dr_or_cr"], d["allocated_amount"])
|
||||
new_row.set('debit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'credit',
|
||||
d["allocated_amount"] * flt(jv_detail.exchange_rate))
|
||||
|
||||
ch.set('credit_in_account_currency' if d['dr_or_cr']== 'debit_in_account_currency'
|
||||
else 'debit_in_account_currency', 0)
|
||||
ch.set('credit' if d['dr_or_cr']== 'debit_in_account_currency' else 'debit', 0)
|
||||
new_row.set('credit_in_account_currency' if d['dr_or_cr'] == 'debit_in_account_currency'
|
||||
else 'debit_in_account_currency', 0)
|
||||
new_row.set('credit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'debit', 0)
|
||||
|
||||
ch.against_account = cstr(jvd[0]["against_account"])
|
||||
ch.reference_type = original_reference_type
|
||||
ch.reference_name = original_reference_name
|
||||
ch.is_advance = cstr(jvd[0]["is_advance"])
|
||||
ch.docstatus = 1
|
||||
new_row.set("reference_type", d["against_voucher_type"])
|
||||
new_row.set("reference_name", d["against_voucher"])
|
||||
|
||||
new_row.against_account = cstr(jv_detail.against_account)
|
||||
new_row.is_advance = cstr(jv_detail.is_advance)
|
||||
new_row.docstatus = 1
|
||||
|
||||
# will work as update after submit
|
||||
jv_obj.flags.ignore_validate_update_after_submit = True
|
||||
jv_obj.save(ignore_permissions=True)
|
||||
journal_entry.flags.ignore_validate_update_after_submit = True
|
||||
if not do_not_save:
|
||||
journal_entry.save(ignore_permissions=True)
|
||||
|
||||
def update_reference_in_payment_entry(d, payment_entry, do_not_save=False):
|
||||
reference_details = {
|
||||
@@ -576,7 +576,7 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_company_default(company, fieldname, ignore_validation=False):
|
||||
value = frappe.get_cached_value('Company', company, fieldname)
|
||||
value = frappe.get_cached_value('Company', company, fieldname)
|
||||
|
||||
if not ignore_validation and not value:
|
||||
throw(_("Please set default {0} in Company {1}")
|
||||
|
||||
Reference in New Issue
Block a user