mirror of
https://github.com/frappe/erpnext.git
synced 2026-02-16 16:15:02 +00:00
* fix: multiple fixes for advance payment accounting
(cherry picked from commit e70caedddc)
# Conflicts:
# erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
# erpnext/accounts/doctype/payment_entry/payment_entry.py
# erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
# erpnext/accounts/utils.py
# erpnext/controllers/accounts_controller.py
# erpnext/patches/v15_0/create_advance_payment_ledger_records.py
* chore: resolve conflicts
* fix: do not execute patch if no advance doctypes
---------
Co-authored-by: Lakshit Jain <108322669+ljain112@users.noreply.github.com>
Co-authored-by: ljain112 <ljain112@gmail.com>
This commit is contained in:
@@ -12,7 +12,8 @@
|
||||
"against_voucher_no",
|
||||
"amount",
|
||||
"currency",
|
||||
"event"
|
||||
"event",
|
||||
"delinked"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -68,12 +69,20 @@
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "delinked",
|
||||
"fieldtype": "Check",
|
||||
"label": "DeLinked",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"in_create": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2024-11-05 10:31:28.736671",
|
||||
"modified": "2025-07-29 11:37:42.678556",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Advance Payment Ledger Entry",
|
||||
@@ -107,7 +116,8 @@
|
||||
"share": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
from erpnext.accounts.utils import update_voucher_outstanding
|
||||
|
||||
|
||||
class AdvancePaymentLedgerEntry(Document):
|
||||
# begin: auto-generated types
|
||||
@@ -19,9 +21,16 @@ class AdvancePaymentLedgerEntry(Document):
|
||||
amount: DF.Currency
|
||||
company: DF.Link | None
|
||||
currency: DF.Link | None
|
||||
delinked: DF.Check
|
||||
event: DF.Data | None
|
||||
voucher_no: DF.DynamicLink | None
|
||||
voucher_type: DF.Link | None
|
||||
# end: auto-generated types
|
||||
|
||||
pass
|
||||
def on_update(self):
|
||||
if (
|
||||
self.against_voucher_type in ["Purchase Order", "Sales Order"]
|
||||
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, None, None, None)
|
||||
|
||||
@@ -191,8 +191,6 @@ class JournalEntry(AccountsController):
|
||||
self.validate_cheque_info()
|
||||
self.check_credit_limit()
|
||||
self.make_gl_entries()
|
||||
self.make_advance_payment_ledger_entries()
|
||||
self.update_advance_paid()
|
||||
self.update_asset_value()
|
||||
self.update_inter_company_jv()
|
||||
self.update_invoice_discounting()
|
||||
@@ -225,8 +223,6 @@ class JournalEntry(AccountsController):
|
||||
"Advance Payment Ledger Entry",
|
||||
)
|
||||
self.make_gl_entries(1)
|
||||
self.make_advance_payment_ledger_entries()
|
||||
self.update_advance_paid()
|
||||
self.unlink_advance_entry_reference()
|
||||
self.unlink_asset_reference()
|
||||
self.unlink_inter_company_jv()
|
||||
@@ -237,18 +233,6 @@ class JournalEntry(AccountsController):
|
||||
def get_title(self):
|
||||
return self.pay_to_recd_from or self.accounts[0].account
|
||||
|
||||
def update_advance_paid(self):
|
||||
advance_paid = frappe._dict()
|
||||
advance_payment_doctypes = get_advance_payment_doctypes()
|
||||
for d in self.get("accounts"):
|
||||
if d.is_advance:
|
||||
if d.reference_type in advance_payment_doctypes:
|
||||
advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
|
||||
|
||||
for voucher_type, order_list in advance_paid.items():
|
||||
for voucher_no in list(set(order_list)):
|
||||
frappe.get_doc(voucher_type, voucher_no).set_total_advance_paid()
|
||||
|
||||
def validate_inter_company_accounts(self):
|
||||
if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
|
||||
doc = frappe.db.get_value(
|
||||
@@ -1094,49 +1078,65 @@ class JournalEntry(AccountsController):
|
||||
self.transaction_exchange_rate = row.exchange_rate
|
||||
break
|
||||
|
||||
advance_doctypes = get_advance_payment_doctypes()
|
||||
|
||||
for d in self.get("accounts"):
|
||||
if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"):
|
||||
r = [d.user_remark, self.remark]
|
||||
r = [x for x in r if x]
|
||||
remarks = "\n".join(r)
|
||||
|
||||
row = {
|
||||
"account": d.account,
|
||||
"party_type": d.party_type,
|
||||
"due_date": self.due_date,
|
||||
"party": d.party,
|
||||
"against": d.against_account,
|
||||
"debit": flt(d.debit, d.precision("debit")),
|
||||
"credit": flt(d.credit, d.precision("credit")),
|
||||
"account_currency": d.account_currency,
|
||||
"debit_in_account_currency": flt(
|
||||
d.debit_in_account_currency, d.precision("debit_in_account_currency")
|
||||
),
|
||||
"credit_in_account_currency": flt(
|
||||
d.credit_in_account_currency, d.precision("credit_in_account_currency")
|
||||
),
|
||||
"transaction_currency": self.transaction_currency,
|
||||
"transaction_exchange_rate": self.transaction_exchange_rate,
|
||||
"debit_in_transaction_currency": flt(
|
||||
d.debit_in_account_currency, d.precision("debit_in_account_currency")
|
||||
)
|
||||
if self.transaction_currency == d.account_currency
|
||||
else flt(d.debit, d.precision("debit")) / self.transaction_exchange_rate,
|
||||
"credit_in_transaction_currency": flt(
|
||||
d.credit_in_account_currency, d.precision("credit_in_account_currency")
|
||||
)
|
||||
if self.transaction_currency == d.account_currency
|
||||
else flt(d.credit, d.precision("credit")) / self.transaction_exchange_rate,
|
||||
"against_voucher_type": d.reference_type,
|
||||
"against_voucher": d.reference_name,
|
||||
"remarks": remarks,
|
||||
"voucher_detail_no": d.reference_detail_no,
|
||||
"cost_center": d.cost_center,
|
||||
"project": d.project,
|
||||
"finance_book": self.finance_book,
|
||||
"advance_voucher_type": d.advance_voucher_type,
|
||||
"advance_voucher_no": d.advance_voucher_no,
|
||||
}
|
||||
|
||||
if d.reference_type in advance_doctypes:
|
||||
row.update(
|
||||
{
|
||||
"against_voucher_type": self.doctype,
|
||||
"against_voucher": self.name,
|
||||
"advance_voucher_type": d.reference_type,
|
||||
"advance_voucher_no": d.reference_name,
|
||||
}
|
||||
)
|
||||
|
||||
gl_map.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": d.account,
|
||||
"party_type": d.party_type,
|
||||
"due_date": self.due_date,
|
||||
"party": d.party,
|
||||
"against": d.against_account,
|
||||
"debit": flt(d.debit, d.precision("debit")),
|
||||
"credit": flt(d.credit, d.precision("credit")),
|
||||
"account_currency": d.account_currency,
|
||||
"debit_in_account_currency": flt(
|
||||
d.debit_in_account_currency, d.precision("debit_in_account_currency")
|
||||
),
|
||||
"credit_in_account_currency": flt(
|
||||
d.credit_in_account_currency, d.precision("credit_in_account_currency")
|
||||
),
|
||||
"transaction_currency": self.transaction_currency,
|
||||
"transaction_exchange_rate": self.transaction_exchange_rate,
|
||||
"debit_in_transaction_currency": flt(
|
||||
d.debit_in_account_currency, d.precision("debit_in_account_currency")
|
||||
)
|
||||
if self.transaction_currency == d.account_currency
|
||||
else flt(d.debit, d.precision("debit")) / self.transaction_exchange_rate,
|
||||
"credit_in_transaction_currency": flt(
|
||||
d.credit_in_account_currency, d.precision("credit_in_account_currency")
|
||||
)
|
||||
if self.transaction_currency == d.account_currency
|
||||
else flt(d.credit, d.precision("credit")) / self.transaction_exchange_rate,
|
||||
"against_voucher_type": d.reference_type,
|
||||
"against_voucher": d.reference_name,
|
||||
"remarks": remarks,
|
||||
"voucher_detail_no": d.reference_detail_no,
|
||||
"cost_center": d.cost_center,
|
||||
"project": d.project,
|
||||
"finance_book": self.finance_book,
|
||||
},
|
||||
row,
|
||||
item=d,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
"reference_name",
|
||||
"reference_due_date",
|
||||
"reference_detail_no",
|
||||
"advance_voucher_type",
|
||||
"advance_voucher_no",
|
||||
"col_break3",
|
||||
"is_advance",
|
||||
"user_remark",
|
||||
@@ -263,12 +265,28 @@
|
||||
"label": "Reference Detail No",
|
||||
"no_copy": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "advance_voucher_type",
|
||||
"fieldtype": "Link",
|
||||
"label": "Advance Voucher Type",
|
||||
"no_copy": 1,
|
||||
"options": "DocType",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "advance_voucher_no",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Advance Voucher No",
|
||||
"no_copy": 1,
|
||||
"options": "advance_voucher_type",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-02-05 01:10:50.224840",
|
||||
"modified": "2025-07-25 04:45:28.117715",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Journal Entry Account",
|
||||
@@ -279,4 +297,4 @@
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,9 @@ class JournalEntryAccount(Document):
|
||||
account: DF.Link
|
||||
account_currency: DF.Link | None
|
||||
account_type: DF.Data | None
|
||||
advance_voucher_no: DF.DynamicLink | None
|
||||
advance_voucher_type: DF.Link | None
|
||||
against_account: DF.Text | None
|
||||
balance: DF.Currency
|
||||
bank_account: DF.Link | None
|
||||
cost_center: DF.Link | None
|
||||
credit: DF.Currency
|
||||
@@ -31,7 +32,6 @@ class JournalEntryAccount(Document):
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
party: DF.DynamicLink | None
|
||||
party_balance: DF.Currency
|
||||
party_type: DF.Link | None
|
||||
project: DF.Link | None
|
||||
reference_detail_no: DF.Data | None
|
||||
|
||||
@@ -117,12 +117,10 @@ class PaymentEntry(AccountsController):
|
||||
def on_submit(self):
|
||||
if self.difference_amount:
|
||||
frappe.throw(_("Difference Amount must be zero"))
|
||||
self.update_payment_requests()
|
||||
self.make_gl_entries()
|
||||
self.update_outstanding_amounts()
|
||||
self.update_payment_schedule()
|
||||
self.update_payment_requests()
|
||||
self.make_advance_payment_ledger_entries()
|
||||
self.update_advance_paid() # advance_paid_status depends on the payment request amount
|
||||
self.set_status()
|
||||
|
||||
def validate_for_repost(self):
|
||||
@@ -222,13 +220,11 @@ class PaymentEntry(AccountsController):
|
||||
"Advance Payment Ledger Entry",
|
||||
)
|
||||
super().on_cancel()
|
||||
self.update_payment_requests(cancel=True)
|
||||
self.make_gl_entries(cancel=1)
|
||||
self.update_outstanding_amounts()
|
||||
self.delink_advance_entry_references()
|
||||
self.update_payment_schedule(cancel=1)
|
||||
self.update_payment_requests(cancel=True)
|
||||
self.make_advance_payment_ledger_entries()
|
||||
self.update_advance_paid() # advance_paid_status depends on the payment request amount
|
||||
self.set_status()
|
||||
|
||||
def update_payment_requests(self, cancel=False):
|
||||
@@ -1366,23 +1362,27 @@ class PaymentEntry(AccountsController):
|
||||
dr_or_cr + "_in_transaction_currency": d.allocated_amount
|
||||
if self.transaction_currency == self.party_account_currency
|
||||
else allocated_amount_in_company_currency / self.transaction_exchange_rate,
|
||||
"advance_voucher_type": d.advance_voucher_type,
|
||||
"advance_voucher_no": d.advance_voucher_no,
|
||||
},
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
|
||||
if self.book_advance_payments_in_separate_party_account:
|
||||
if d.reference_doctype in advance_payment_doctypes:
|
||||
# Upon reconciliation, whole ledger will be reposted. So, reference to SO/PO is fine
|
||||
gle.update(
|
||||
{
|
||||
"against_voucher_type": d.reference_doctype,
|
||||
"against_voucher": d.reference_name,
|
||||
}
|
||||
)
|
||||
else:
|
||||
# Do not reference Invoices while Advance is in separate party account
|
||||
gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name})
|
||||
if d.reference_doctype in advance_payment_doctypes:
|
||||
# advance reference
|
||||
gle.update(
|
||||
{
|
||||
"against_voucher_type": self.doctype,
|
||||
"against_voucher": self.name,
|
||||
"advance_voucher_type": d.reference_doctype,
|
||||
"advance_voucher_no": d.reference_name,
|
||||
}
|
||||
)
|
||||
|
||||
elif self.book_advance_payments_in_separate_party_account:
|
||||
# Do not reference Invoices while Advance is in separate party account
|
||||
gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name})
|
||||
else:
|
||||
gle.update(
|
||||
{
|
||||
@@ -1512,6 +1512,8 @@ class PaymentEntry(AccountsController):
|
||||
{
|
||||
"against_voucher_type": invoice.reference_doctype,
|
||||
"against_voucher": invoice.reference_name,
|
||||
"advance_voucher_type": invoice.advance_voucher_type,
|
||||
"advance_voucher_no": invoice.advance_voucher_no,
|
||||
"posting_date": posting_date,
|
||||
}
|
||||
)
|
||||
@@ -1536,6 +1538,8 @@ class PaymentEntry(AccountsController):
|
||||
{
|
||||
"against_voucher_type": "Payment Entry",
|
||||
"against_voucher": self.name,
|
||||
"advance_voucher_type": invoice.advance_voucher_type,
|
||||
"advance_voucher_no": invoice.advance_voucher_no,
|
||||
}
|
||||
)
|
||||
gle = self.get_gl_dict(
|
||||
@@ -1684,17 +1688,6 @@ class PaymentEntry(AccountsController):
|
||||
|
||||
return flt(gl_dict.get(field, 0) / (conversion_rate or 1))
|
||||
|
||||
def update_advance_paid(self):
|
||||
if self.payment_type not in ("Receive", "Pay") or not self.party:
|
||||
return
|
||||
|
||||
advance_payment_doctypes = get_advance_payment_doctypes()
|
||||
for d in self.get("references"):
|
||||
if d.allocated_amount and d.reference_doctype in advance_payment_doctypes:
|
||||
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):
|
||||
self.reference_no = reference_doc.name
|
||||
self.reference_date = nowdate()
|
||||
|
||||
@@ -52,7 +52,7 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
self.assertEqual(pe.paid_to_account_type, "Cash")
|
||||
|
||||
expected_gle = dict(
|
||||
(d[0], d) for d in [["Debtors - _TC", 0, 1000, so.name], ["_Test Cash - _TC", 1000.0, 0, None]]
|
||||
(d[0], d) for d in [["Debtors - _TC", 0, 1000, pe.name], ["_Test Cash - _TC", 1000.0, 0, None]]
|
||||
)
|
||||
|
||||
self.validate_gl_entries(pe.name, expected_gle)
|
||||
@@ -84,7 +84,7 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
|
||||
expected_gle = dict(
|
||||
(d[0], d)
|
||||
for d in [["_Test Receivable USD - _TC", 0, 5500, so.name], [pe.paid_to, 5500.0, 0, None]]
|
||||
for d in [["_Test Receivable USD - _TC", 0, 5500, pe.name], [pe.paid_to, 5500.0, 0, None]]
|
||||
)
|
||||
|
||||
self.validate_gl_entries(pe.name, expected_gle)
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
"exchange_gain_loss",
|
||||
"account",
|
||||
"payment_request",
|
||||
"payment_request_outstanding"
|
||||
"payment_request_outstanding",
|
||||
"advance_voucher_type",
|
||||
"advance_voucher_no"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -151,12 +153,28 @@
|
||||
"fieldtype": "Date",
|
||||
"label": "Reconcile Effect On",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "advance_voucher_type",
|
||||
"fieldtype": "Link",
|
||||
"label": "Advance Voucher Type",
|
||||
"options": "DocType",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "advance_voucher_no",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Advance Voucher No",
|
||||
"options": "advance_voucher_type",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-01-13 15:56:18.895082",
|
||||
"modified": "2025-07-25 04:32:11.040025",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry Reference",
|
||||
@@ -167,4 +185,4 @@
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ class PaymentEntryReference(Document):
|
||||
|
||||
account: DF.Link | None
|
||||
account_type: DF.Data | None
|
||||
advance_voucher_no: DF.DynamicLink | None
|
||||
advance_voucher_type: DF.Link | None
|
||||
allocated_amount: DF.Float
|
||||
bill_no: DF.Data | None
|
||||
due_date: DF.Date | None
|
||||
@@ -26,7 +28,6 @@ class PaymentEntryReference(Document):
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
payment_request: DF.Link | None
|
||||
payment_request_outstanding: DF.Float
|
||||
payment_term: DF.Link | None
|
||||
payment_term_outstanding: DF.Float
|
||||
payment_type: DF.Data | None
|
||||
|
||||
@@ -197,4 +197,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2206,6 +2206,136 @@ class TestPaymentReconciliation(FrappeTestCase):
|
||||
self.assertEqual(len(pr.get("payments")), 0)
|
||||
self.assertEqual(pr.get("invoices")[0].get("outstanding_amount"), 200)
|
||||
|
||||
def test_partial_advance_payment_with_closed_fiscal_year(self):
|
||||
"""
|
||||
Test Advance Payment partial reconciliation before period closing and partial after period closing
|
||||
"""
|
||||
default_settings = frappe.db.get_value(
|
||||
"Company",
|
||||
self.company,
|
||||
[
|
||||
"book_advance_payments_in_separate_party_account",
|
||||
"default_advance_paid_account",
|
||||
"reconciliation_takes_effect_on",
|
||||
],
|
||||
as_dict=True,
|
||||
)
|
||||
first_fy_start_date = frappe.db.get_value("Fiscal Year", {"disabled": 0}, "min(year_start_date)")
|
||||
prev_fy_start_date = add_years(first_fy_start_date, -1)
|
||||
prev_fy_end_date = add_days(first_fy_start_date, -1)
|
||||
|
||||
create_fiscal_year(
|
||||
company=self.company, year_start_date=prev_fy_start_date, year_end_date=prev_fy_end_date
|
||||
)
|
||||
|
||||
frappe.db.set_value(
|
||||
"Company",
|
||||
self.company,
|
||||
{
|
||||
"book_advance_payments_in_separate_party_account": 1,
|
||||
"default_advance_paid_account": self.advance_payable_account,
|
||||
"reconciliation_takes_effect_on": "Oldest Of Invoice Or Advance",
|
||||
},
|
||||
)
|
||||
|
||||
self.supplier = "_Test Supplier"
|
||||
|
||||
# Create advance payment of 1000 (previous FY)
|
||||
pe = self.create_payment_entry(amount=1000, posting_date=prev_fy_start_date)
|
||||
pe.party_type = "Supplier"
|
||||
pe.party = self.supplier
|
||||
pe.payment_type = "Pay"
|
||||
pe.paid_from = self.cash
|
||||
pe.paid_to = self.advance_payable_account
|
||||
pe.save().submit()
|
||||
|
||||
# Create purchase invoice of 600 (previous FY)
|
||||
pi1 = self.create_purchase_invoice(qty=1, rate=600, do_not_submit=True)
|
||||
pi1.posting_date = prev_fy_start_date
|
||||
pi1.set_posting_time = 1
|
||||
pi1.supplier = self.supplier
|
||||
pi1.credit_to = self.creditors
|
||||
pi1.save().submit()
|
||||
|
||||
# Reconcile advance payment
|
||||
pr = self.create_payment_reconciliation(party_is_customer=False)
|
||||
pr.party = self.supplier
|
||||
pr.receivable_payable_account = self.creditors
|
||||
pr.default_advance_account = self.advance_payable_account
|
||||
pr.from_invoice_date = pr.to_invoice_date = pi1.posting_date
|
||||
pr.from_payment_date = pr.to_payment_date = pe.posting_date
|
||||
pr.get_unreconciled_entries()
|
||||
invoices = [x.as_dict() for x in pr.invoices if x.invoice_number == pi1.name]
|
||||
payments = [x.as_dict() for x in pr.payments if x.reference_name == pe.name]
|
||||
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||
pr.reconcile()
|
||||
|
||||
# Verify partial reconciliation
|
||||
pe.reload()
|
||||
pi1.reload()
|
||||
|
||||
self.assertEqual(len(pe.references), 1)
|
||||
self.assertEqual(pe.references[0].allocated_amount, 600)
|
||||
self.assertEqual(flt(pe.unallocated_amount), 400)
|
||||
|
||||
self.assertEqual(pi1.outstanding_amount, 0)
|
||||
self.assertEqual(pi1.status, "Paid")
|
||||
|
||||
# Close accounting period for March (previous FY)
|
||||
pcv = make_period_closing_voucher(
|
||||
company=self.company, cost_center=self.cost_center, posting_date=prev_fy_end_date
|
||||
)
|
||||
pcv.reload()
|
||||
self.assertEqual(pcv.gle_processing_status, "Completed")
|
||||
|
||||
# Change reconciliation setting to "Reconciliation Date"
|
||||
frappe.db.set_value(
|
||||
"Company",
|
||||
self.company,
|
||||
"reconciliation_takes_effect_on",
|
||||
"Reconciliation Date",
|
||||
)
|
||||
|
||||
# Create new purchase invoice for 400 in new fiscal year
|
||||
pi2 = self.create_purchase_invoice(qty=1, rate=400, do_not_submit=True)
|
||||
pi2.posting_date = today()
|
||||
pi2.set_posting_time = 1
|
||||
pi2.supplier = self.supplier
|
||||
pi2.currency = "INR"
|
||||
pi2.credit_to = self.creditors
|
||||
pi2.save()
|
||||
pi2.submit()
|
||||
|
||||
# Allocate 600 from advance payment to purchase invoice
|
||||
pr = self.create_payment_reconciliation(party_is_customer=False)
|
||||
pr.party = self.supplier
|
||||
pr.receivable_payable_account = self.creditors
|
||||
pr.default_advance_account = self.advance_payable_account
|
||||
pr.from_invoice_date = pr.to_invoice_date = pi2.posting_date
|
||||
pr.from_payment_date = pr.to_payment_date = pe.posting_date
|
||||
pr.get_unreconciled_entries()
|
||||
invoices = [x.as_dict() for x in pr.invoices if x.invoice_number == pi2.name]
|
||||
payments = [x.as_dict() for x in pr.payments if x.reference_name == pe.name]
|
||||
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||
pr.reconcile()
|
||||
|
||||
pe.reload()
|
||||
pi2.reload()
|
||||
|
||||
# Assert advance payment is fully allocated
|
||||
self.assertEqual(len(pe.references), 2)
|
||||
self.assertEqual(flt(pe.unallocated_amount), 0)
|
||||
|
||||
# Assert new invoice is fully paid
|
||||
self.assertEqual(pi2.outstanding_amount, 0)
|
||||
self.assertEqual(pi2.status, "Paid")
|
||||
|
||||
# Verify reconciliation dates are correct based on company setting
|
||||
self.assertEqual(getdate(pe.references[0].reconcile_effect_on), getdate(pi1.posting_date))
|
||||
self.assertEqual(getdate(pe.references[1].reconcile_effect_on), getdate(pi2.posting_date))
|
||||
|
||||
frappe.db.set_value("Company", self.company, default_settings)
|
||||
|
||||
|
||||
def make_customer(customer_name, currency=None):
|
||||
if not frappe.db.exists("Customer", customer_name):
|
||||
|
||||
@@ -328,7 +328,7 @@ class TestPaymentRequest(FrappeTestCase):
|
||||
|
||||
self.assertEqual(pe.paid_amount, 800) # paid amount set from pr's outstanding amount
|
||||
self.assertEqual(pe.references[0].allocated_amount, 800)
|
||||
self.assertEqual(pe.references[0].outstanding_amount, 800) # for Orders it is not zero
|
||||
self.assertEqual(pe.references[0].outstanding_amount, 0) # Also for orders it will zero
|
||||
self.assertEqual(pe.references[0].payment_request, pr.name)
|
||||
|
||||
so.load_from_db()
|
||||
|
||||
@@ -169,6 +169,10 @@ def start_repost(account_repost_doc=str) -> None:
|
||||
frappe.db.delete(
|
||||
"Payment Ledger Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name}
|
||||
)
|
||||
frappe.db.delete(
|
||||
"Advance Payment Ledger Entry",
|
||||
filters={"voucher_type": doc.doctype, "voucher_no": doc.name},
|
||||
)
|
||||
|
||||
if doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
if not repost_doc.delete_cancelled_entries:
|
||||
|
||||
@@ -8,7 +8,7 @@ from frappe import _, qb
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder.custom import ConstantColumn
|
||||
|
||||
from erpnext.accounts.utils import _delete_pl_entries, create_payment_ledger_entry
|
||||
from erpnext.accounts.utils import _delete_adv_pl_entries, _delete_pl_entries, create_payment_ledger_entry
|
||||
|
||||
VOUCHER_TYPES = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]
|
||||
|
||||
@@ -16,6 +16,7 @@ VOUCHER_TYPES = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal
|
||||
def repost_ple_for_voucher(voucher_type, voucher_no, gle_map=None):
|
||||
if voucher_type and voucher_no and gle_map:
|
||||
_delete_pl_entries(voucher_type, voucher_no)
|
||||
_delete_adv_pl_entries(voucher_type, voucher_no)
|
||||
create_payment_ledger_entry(gle_map, cancel=0)
|
||||
|
||||
|
||||
|
||||
@@ -465,10 +465,26 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
|
||||
self.assertEqual(len(pr.get("invoices")), 0)
|
||||
self.assertEqual(len(pr.get("payments")), 0)
|
||||
|
||||
# Assert 'Advance Paid'
|
||||
so.reload()
|
||||
self.assertEqual(so.advance_paid, 1000)
|
||||
|
||||
unreconcile = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Unreconcile Payment",
|
||||
"company": self.company,
|
||||
"voucher_type": pe.doctype,
|
||||
"voucher_no": pe.name,
|
||||
}
|
||||
)
|
||||
unreconcile.add_references()
|
||||
unreconcile.allocations = [x for x in unreconcile.allocations if x.reference_name == si.name]
|
||||
unreconcile.save().submit()
|
||||
|
||||
# after unreconcilaition advance paid will be reduced
|
||||
# Assert 'Advance Paid'
|
||||
so.reload()
|
||||
self.assertEqual(so.advance_paid, 0)
|
||||
|
||||
self.disable_advance_as_liability()
|
||||
|
||||
def test_unreconcile_advance_from_journal_entry(self):
|
||||
|
||||
@@ -12,7 +12,6 @@ from frappe.utils.data import comma_and
|
||||
|
||||
from erpnext.accounts.utils import (
|
||||
cancel_exchange_gain_loss_journal,
|
||||
get_advance_payment_doctypes,
|
||||
unlink_ref_doc_from_payment_entries,
|
||||
update_voucher_outstanding,
|
||||
)
|
||||
@@ -45,31 +44,12 @@ class UnreconcilePayment(Document):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_allocations_from_payment(self):
|
||||
allocated_references = []
|
||||
ple = qb.DocType("Payment Ledger Entry")
|
||||
allocated_references = (
|
||||
qb.from_(ple)
|
||||
.select(
|
||||
ple.account,
|
||||
ple.party_type,
|
||||
ple.party,
|
||||
ple.against_voucher_type.as_("reference_doctype"),
|
||||
ple.against_voucher_no.as_("reference_name"),
|
||||
Abs(Sum(ple.amount_in_account_currency)).as_("allocated_amount"),
|
||||
ple.account_currency,
|
||||
)
|
||||
.where(
|
||||
(ple.docstatus == 1)
|
||||
& (ple.voucher_type == self.voucher_type)
|
||||
& (ple.voucher_no == self.voucher_no)
|
||||
& (ple.voucher_no != ple.against_voucher_no)
|
||||
)
|
||||
.groupby(ple.against_voucher_type, ple.against_voucher_no)
|
||||
.run(as_dict=True)
|
||||
return get_linked_payments_for_doc(
|
||||
company=self.company,
|
||||
doctype=self.voucher_type,
|
||||
docname=self.voucher_no,
|
||||
)
|
||||
|
||||
return allocated_references
|
||||
|
||||
def add_references(self):
|
||||
allocations = self.get_allocations_from_payment()
|
||||
|
||||
@@ -82,42 +62,43 @@ class UnreconcilePayment(Document):
|
||||
doc = frappe.get_doc(alloc.reference_doctype, alloc.reference_name)
|
||||
unlink_ref_doc_from_payment_entries(doc, self.voucher_no)
|
||||
cancel_exchange_gain_loss_journal(doc, self.voucher_type, self.voucher_no)
|
||||
|
||||
# update outstanding amounts
|
||||
update_voucher_outstanding(
|
||||
alloc.reference_doctype, alloc.reference_name, alloc.account, alloc.party_type, alloc.party
|
||||
alloc.reference_doctype,
|
||||
alloc.reference_name,
|
||||
alloc.account,
|
||||
alloc.party_type,
|
||||
alloc.party,
|
||||
)
|
||||
if doc.doctype in get_advance_payment_doctypes():
|
||||
self.make_advance_payment_ledger(alloc)
|
||||
doc.set_total_advance_paid()
|
||||
|
||||
frappe.db.set_value("Unreconcile Payment Entries", alloc.name, "unlinked", True)
|
||||
|
||||
def make_advance_payment_ledger(self, alloc):
|
||||
if alloc.allocated_amount > 0:
|
||||
doc = frappe.new_doc("Advance Payment Ledger Entry")
|
||||
doc.company = self.company
|
||||
doc.voucher_type = self.voucher_type
|
||||
doc.voucher_no = self.voucher_no
|
||||
doc.against_voucher_type = alloc.reference_doctype
|
||||
doc.against_voucher_no = alloc.reference_name
|
||||
doc.amount = -1 * alloc.allocated_amount
|
||||
doc.event = "Unreconcile"
|
||||
doc.currency = alloc.account_currency
|
||||
doc.flags.ignore_permissions = 1
|
||||
doc.save()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def doc_has_references(doctype: str | None = None, docname: str | None = None):
|
||||
count = 0
|
||||
if doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
return frappe.db.count(
|
||||
count = frappe.db.count(
|
||||
"Payment Ledger Entry",
|
||||
filters={"delinked": 0, "against_voucher_no": docname, "amount": ["<", 0]},
|
||||
)
|
||||
else:
|
||||
return frappe.db.count(
|
||||
count = frappe.db.count(
|
||||
"Payment Ledger Entry",
|
||||
filters={"delinked": 0, "voucher_no": docname, "against_voucher_no": ["!=", docname]},
|
||||
)
|
||||
count += frappe.db.count(
|
||||
"Advance Payment Ledger Entry",
|
||||
filters={
|
||||
"delinked": 0,
|
||||
"voucher_no": docname,
|
||||
"voucher_type": doctype,
|
||||
"event": ["=", "Submit"],
|
||||
},
|
||||
)
|
||||
|
||||
return count
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -139,9 +120,12 @@ def get_linked_payments_for_doc(
|
||||
res = (
|
||||
qb.from_(ple)
|
||||
.select(
|
||||
ple.account,
|
||||
ple.party_type,
|
||||
ple.party,
|
||||
ple.company,
|
||||
ple.voucher_type,
|
||||
ple.voucher_no,
|
||||
ple.voucher_type.as_("reference_doctype"),
|
||||
ple.voucher_no.as_("reference_name"),
|
||||
Abs(Sum(ple.amount_in_account_currency)).as_("allocated_amount"),
|
||||
ple.account_currency,
|
||||
)
|
||||
@@ -163,19 +147,52 @@ def get_linked_payments_for_doc(
|
||||
qb.from_(ple)
|
||||
.select(
|
||||
ple.company,
|
||||
ple.against_voucher_type.as_("voucher_type"),
|
||||
ple.against_voucher_no.as_("voucher_no"),
|
||||
ple.account,
|
||||
ple.party_type,
|
||||
ple.party,
|
||||
ple.against_voucher_type.as_("reference_doctype"),
|
||||
ple.against_voucher_no.as_("reference_name"),
|
||||
Abs(Sum(ple.amount_in_account_currency)).as_("allocated_amount"),
|
||||
ple.account_currency,
|
||||
)
|
||||
.where(Criterion.all(criteria))
|
||||
.groupby(ple.against_voucher_no)
|
||||
)
|
||||
|
||||
res = query.run(as_dict=True)
|
||||
|
||||
res += get_linked_advances(company, _dn)
|
||||
|
||||
return res
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def get_linked_advances(company, docname):
|
||||
adv = qb.DocType("Advance Payment Ledger Entry")
|
||||
criteria = [
|
||||
(adv.company == company),
|
||||
(adv.delinked == 0),
|
||||
(adv.voucher_no == docname),
|
||||
(adv.event == "Submit"),
|
||||
]
|
||||
|
||||
return (
|
||||
qb.from_(adv)
|
||||
.select(
|
||||
adv.company,
|
||||
adv.against_voucher_type.as_("reference_doctype"),
|
||||
adv.against_voucher_no.as_("reference_name"),
|
||||
Abs(Sum(adv.amount)).as_("allocated_amount"),
|
||||
adv.currency,
|
||||
)
|
||||
.where(Criterion.all(criteria))
|
||||
.having(qb.Field("allocated_amount") > 0)
|
||||
.groupby(adv.against_voucher_no)
|
||||
.run(as_dict=True)
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_unreconcile_doc_for_selection(selections=None):
|
||||
if selections:
|
||||
|
||||
@@ -305,6 +305,8 @@ def get_merge_properties(dimensions=None):
|
||||
"project",
|
||||
"finance_book",
|
||||
"voucher_no",
|
||||
"advance_voucher_type",
|
||||
"advance_voucher_no",
|
||||
]
|
||||
if dimensions:
|
||||
merge_properties.extend(dimensions)
|
||||
|
||||
@@ -471,42 +471,27 @@ def reconcile_against_document(
|
||||
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]
|
||||
voucher_type, voucher_no = key
|
||||
|
||||
# cancel advance entry
|
||||
doc = frappe.get_doc(voucher_type, voucher_no)
|
||||
frappe.flags.ignore_party_validation = True
|
||||
|
||||
# When Advance is allocated from an Order to an Invoice
|
||||
# whole ledger must be reposted
|
||||
repost_whole_ledger = any([x.voucher_detail_no for x in entries])
|
||||
if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account:
|
||||
if repost_whole_ledger:
|
||||
doc.make_gl_entries(cancel=1)
|
||||
else:
|
||||
doc.make_advance_gl_entries(cancel=1)
|
||||
else:
|
||||
_delete_pl_entries(voucher_type, voucher_no)
|
||||
|
||||
reposting_rows = []
|
||||
for entry in entries:
|
||||
check_if_advance_entry_modified(entry)
|
||||
validate_allocated_amount(entry)
|
||||
|
||||
dimensions_dict = _build_dimensions_dict_for_exc_gain_loss(entry, active_dimensions)
|
||||
|
||||
# update ref in advance entry
|
||||
if voucher_type == "Journal Entry":
|
||||
referenced_row, update_advance_paid = update_reference_in_journal_entry(
|
||||
entry, doc, do_not_save=False
|
||||
)
|
||||
referenced_row = update_reference_in_journal_entry(entry, doc, do_not_save=False)
|
||||
# advance section in sales/purchase invoice and reconciliation tool,both pass on exchange gain/loss
|
||||
# amount and account in args
|
||||
# referenced_row is used to deduplicate gain/loss journal
|
||||
entry.update({"referenced_row": referenced_row})
|
||||
entry.update({"referenced_row": referenced_row.name})
|
||||
doc.make_exchange_gain_loss_journal([entry], dimensions_dict)
|
||||
else:
|
||||
referenced_row, update_advance_paid = update_reference_in_payment_entry(
|
||||
referenced_row = update_reference_in_payment_entry(
|
||||
entry,
|
||||
doc,
|
||||
do_not_save=True,
|
||||
@@ -515,20 +500,16 @@ def reconcile_against_document(
|
||||
)
|
||||
if referenced_row.get("outstanding_amount"):
|
||||
referenced_row.outstanding_amount -= flt(entry.allocated_amount)
|
||||
|
||||
reposting_rows.append(referenced_row)
|
||||
|
||||
doc.save(ignore_permissions=True)
|
||||
# re-submit advance entry
|
||||
doc = frappe.get_doc(entry.voucher_type, entry.voucher_no)
|
||||
|
||||
if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account:
|
||||
# When Advance is allocated from an Order to an Invoice
|
||||
# whole ledger must be reposted
|
||||
if repost_whole_ledger:
|
||||
doc.make_gl_entries()
|
||||
else:
|
||||
# both ledgers must be posted to for `Advance` in separate account feature
|
||||
# TODO: find a more efficient way post only for the new linked vouchers
|
||||
doc.make_advance_gl_entries()
|
||||
for row in reposting_rows:
|
||||
doc.make_advance_gl_entries(entry=row)
|
||||
else:
|
||||
_delete_pl_entries(voucher_type, voucher_no)
|
||||
gl_map = doc.build_gl_map()
|
||||
# Make sure there is no overallocation
|
||||
from erpnext.accounts.general_ledger import process_debit_credit_difference
|
||||
@@ -545,11 +526,6 @@ def reconcile_against_document(
|
||||
entry.party_type,
|
||||
entry.party,
|
||||
)
|
||||
# update advance paid in Advance Receivable/Payable doctypes
|
||||
if update_advance_paid:
|
||||
for t, n in update_advance_paid:
|
||||
frappe.get_doc(t, n).set_total_advance_paid()
|
||||
|
||||
frappe.flags.ignore_party_validation = False
|
||||
|
||||
|
||||
@@ -630,12 +606,6 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
|
||||
"""
|
||||
jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
||||
|
||||
# Update Advance Paid in SO/PO since they might be getting unlinked
|
||||
update_advance_paid = []
|
||||
|
||||
if jv_detail.get("reference_type") in ["Sales Order", "Purchase Order"]:
|
||||
update_advance_paid.append((jv_detail.reference_type, jv_detail.reference_name))
|
||||
|
||||
rev_dr_or_cr = (
|
||||
"debit_in_account_currency"
|
||||
if d["dr_or_cr"] == "credit_in_account_currency"
|
||||
@@ -688,6 +658,10 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
|
||||
new_row.is_advance = cstr(jv_detail.is_advance)
|
||||
new_row.docstatus = 1
|
||||
|
||||
if jv_detail.get("reference_type") in get_advance_payment_doctypes():
|
||||
new_row.advance_voucher_type = jv_detail.get("reference_type")
|
||||
new_row.advance_voucher_no = jv_detail.get("reference_name")
|
||||
|
||||
# will work as update after submit
|
||||
journal_entry.flags.ignore_validate_update_after_submit = True
|
||||
# Ledgers will be reposted by Reconciliation tool
|
||||
@@ -695,7 +669,7 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
|
||||
if not do_not_save:
|
||||
journal_entry.save(ignore_permissions=True)
|
||||
|
||||
return new_row.name, update_advance_paid
|
||||
return new_row
|
||||
|
||||
|
||||
def update_reference_in_payment_entry(
|
||||
@@ -714,7 +688,8 @@ def update_reference_in_payment_entry(
|
||||
"account": d.account,
|
||||
"dimensions": d.dimensions,
|
||||
}
|
||||
update_advance_paid = []
|
||||
|
||||
advance_payment_doctypes = get_advance_payment_doctypes()
|
||||
|
||||
# Update Reconciliation effect date in reference
|
||||
if payment_entry.book_advance_payments_in_separate_party_account:
|
||||
@@ -726,10 +701,6 @@ def update_reference_in_payment_entry(
|
||||
if d.voucher_detail_no:
|
||||
existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
|
||||
|
||||
# Update Advance Paid in SO/PO since they are getting unlinked
|
||||
if existing_row.get("reference_doctype") in ["Sales Order", "Purchase Order"]:
|
||||
update_advance_paid.append((existing_row.reference_doctype, existing_row.reference_name))
|
||||
|
||||
if d.allocated_amount <= existing_row.allocated_amount:
|
||||
existing_row.allocated_amount -= d.allocated_amount
|
||||
|
||||
@@ -737,7 +708,13 @@ def update_reference_in_payment_entry(
|
||||
new_row.docstatus = 1
|
||||
for field in list(reference_details):
|
||||
new_row.set(field, reference_details[field])
|
||||
|
||||
if existing_row.reference_doctype in advance_payment_doctypes:
|
||||
new_row.advance_voucher_type = existing_row.reference_doctype
|
||||
new_row.advance_voucher_no = existing_row.reference_name
|
||||
|
||||
row = new_row
|
||||
|
||||
else:
|
||||
new_row = payment_entry.append("references")
|
||||
new_row.docstatus = 1
|
||||
@@ -771,7 +748,8 @@ def update_reference_in_payment_entry(
|
||||
payment_entry.flags.ignore_reposting_on_reconciliation = True
|
||||
if not do_not_save:
|
||||
payment_entry.save(ignore_permissions=True)
|
||||
return row, update_advance_paid
|
||||
|
||||
return row
|
||||
|
||||
|
||||
def get_reconciliation_effect_date(against_voucher_type, against_voucher, company, posting_date):
|
||||
@@ -949,6 +927,24 @@ def update_accounting_ledgers_after_reference_removal(
|
||||
ple_update_query = ple_update_query.where(ple.voucher_no == payment_name)
|
||||
ple_update_query.run()
|
||||
|
||||
# Advance Payment
|
||||
adv = qb.DocType("Advance Payment Ledger Entry")
|
||||
adv_ple = (
|
||||
qb.update(adv)
|
||||
.set(adv.delinked, 1)
|
||||
.set(adv.modified, now())
|
||||
.set(adv.modified_by, frappe.session.user)
|
||||
.where(adv.delinked == 0)
|
||||
.where(
|
||||
((adv.against_voucher_type == ref_type) & (adv.against_voucher_no == ref_no))
|
||||
| ((adv.voucher_type == ref_type) & (adv.voucher_no == ref_no))
|
||||
)
|
||||
)
|
||||
if payment_name:
|
||||
adv_ple = adv_ple.where(adv.voucher_no == payment_name)
|
||||
|
||||
adv_ple.run()
|
||||
|
||||
|
||||
def remove_ref_from_advance_section(ref_doc: object = None):
|
||||
# TODO: this might need some testing
|
||||
@@ -985,6 +981,8 @@ def remove_ref_doc_link_from_jv(
|
||||
qb.update(jea)
|
||||
.set(jea.reference_type, None)
|
||||
.set(jea.reference_name, None)
|
||||
.set(jea.advance_voucher_type, None)
|
||||
.set(jea.advance_voucher_no, None)
|
||||
.set(jea.modified, now())
|
||||
.set(jea.modified_by, frappe.session.user)
|
||||
.where((jea.reference_type == ref_type) & (jea.reference_name == ref_no))
|
||||
@@ -1495,6 +1493,11 @@ def _delete_pl_entries(voucher_type, voucher_no):
|
||||
qb.from_(ple).delete().where((ple.voucher_type == voucher_type) & (ple.voucher_no == voucher_no)).run()
|
||||
|
||||
|
||||
def _delete_adv_pl_entries(voucher_type, voucher_no):
|
||||
adv = qb.DocType("Advance Payment Ledger Entry")
|
||||
qb.from_(adv).delete().where((adv.voucher_type == voucher_type) & (adv.voucher_no == voucher_no)).run()
|
||||
|
||||
|
||||
def _delete_gl_entries(voucher_type, voucher_no):
|
||||
gle = qb.DocType("GL Entry")
|
||||
qb.from_(gle).delete().where((gle.voucher_type == voucher_type) & (gle.voucher_no == voucher_no)).run()
|
||||
@@ -1814,6 +1817,11 @@ def get_payment_ledger_entries(gl_entries, cancel=0):
|
||||
dr_or_cr *= -1
|
||||
dr_or_cr_account_currency *= -1
|
||||
|
||||
against_voucher_type = (
|
||||
gle.against_voucher_type if gle.against_voucher_type else gle.voucher_type
|
||||
)
|
||||
against_voucher_no = gle.against_voucher if gle.against_voucher else gle.voucher_no
|
||||
|
||||
ple = frappe._dict(
|
||||
doctype="Payment Ledger Entry",
|
||||
posting_date=gle.posting_date,
|
||||
@@ -1828,14 +1836,12 @@ def get_payment_ledger_entries(gl_entries, cancel=0):
|
||||
voucher_type=gle.voucher_type,
|
||||
voucher_no=gle.voucher_no,
|
||||
voucher_detail_no=gle.voucher_detail_no,
|
||||
against_voucher_type=gle.against_voucher_type
|
||||
if gle.against_voucher_type
|
||||
else gle.voucher_type,
|
||||
against_voucher_no=gle.against_voucher if gle.against_voucher else gle.voucher_no,
|
||||
against_voucher_type=against_voucher_type,
|
||||
against_voucher_no=against_voucher_no,
|
||||
account_currency=gle.account_currency,
|
||||
amount=dr_or_cr,
|
||||
amount_in_account_currency=dr_or_cr_account_currency,
|
||||
delinked=True if cancel else False,
|
||||
delinked=cancel,
|
||||
remarks=gle.remarks,
|
||||
)
|
||||
|
||||
@@ -1844,10 +1850,40 @@ def get_payment_ledger_entries(gl_entries, cancel=0):
|
||||
for dimension in dimensions_and_defaults[0]:
|
||||
ple[dimension.fieldname] = gle.get(dimension.fieldname)
|
||||
|
||||
if gle.advance_voucher_no:
|
||||
# create advance entry
|
||||
adv = get_advance_ledger_entry(
|
||||
gle, against_voucher_type, against_voucher_no, dr_or_cr_account_currency, cancel
|
||||
)
|
||||
|
||||
ple_map.append(adv)
|
||||
|
||||
ple_map.append(ple)
|
||||
|
||||
return ple_map
|
||||
|
||||
|
||||
def get_advance_ledger_entry(gle, against_voucher_type, against_voucher_no, amount, cancel):
|
||||
event = (
|
||||
"Submit"
|
||||
if (against_voucher_type == gle.voucher_type and against_voucher_no == gle.voucher_no)
|
||||
else "Adjustment"
|
||||
)
|
||||
return frappe._dict(
|
||||
doctype="Advance Payment Ledger Entry",
|
||||
company=gle.company,
|
||||
voucher_type=gle.voucher_type,
|
||||
voucher_no=gle.voucher_no,
|
||||
voucher_detail_no=gle.voucher_detail_no,
|
||||
against_voucher_type=gle.advance_voucher_type,
|
||||
against_voucher_no=gle.advance_voucher_no,
|
||||
amount=amount,
|
||||
currency=gle.account_currency,
|
||||
event=event,
|
||||
delinked=cancel,
|
||||
)
|
||||
|
||||
|
||||
def create_payment_ledger_entry(
|
||||
gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0, partial_cancel=False
|
||||
):
|
||||
@@ -1868,6 +1904,14 @@ def create_payment_ledger_entry(
|
||||
|
||||
|
||||
def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, party):
|
||||
if not voucher_type or not voucher_no:
|
||||
return
|
||||
|
||||
if voucher_type in ["Purchase Order", "Sales Order"]:
|
||||
ref_doc = frappe.get_lazy_doc(voucher_type, voucher_no)
|
||||
ref_doc.set_total_advance_paid()
|
||||
return
|
||||
|
||||
ple = frappe.qb.DocType("Payment Ledger Entry")
|
||||
vouchers = [frappe._dict({"voucher_type": voucher_type, "voucher_no": voucher_no})]
|
||||
common_filter = []
|
||||
@@ -1910,7 +1954,27 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa
|
||||
|
||||
|
||||
def delink_original_entry(pl_entry, partial_cancel=False):
|
||||
if pl_entry:
|
||||
if not pl_entry:
|
||||
return
|
||||
|
||||
if pl_entry.doctype == "Advance Payment Ledger Entry":
|
||||
adv = qb.DocType("Advance Payment Ledger Entry")
|
||||
|
||||
(
|
||||
qb.update(adv)
|
||||
.set(adv.delinked, 1)
|
||||
.set(adv.event, "Cancel")
|
||||
.set(adv.modified, now())
|
||||
.set(adv.modified_by, frappe.session.user)
|
||||
.where(adv.voucher_type == pl_entry.voucher_type)
|
||||
.where(adv.voucher_no == pl_entry.voucher_no)
|
||||
.where(adv.against_voucher_type == pl_entry.against_voucher_type)
|
||||
.where(adv.against_voucher_no == pl_entry.against_voucher_no)
|
||||
.where(adv.event == pl_entry.event)
|
||||
.run()
|
||||
)
|
||||
|
||||
else:
|
||||
ple = qb.DocType("Payment Ledger Entry")
|
||||
query = (
|
||||
qb.update(ple)
|
||||
|
||||
@@ -505,6 +505,7 @@ class PurchaseOrder(BuyingController):
|
||||
self.ignore_linked_doctypes = (
|
||||
"GL Entry",
|
||||
"Payment Ledger Entry",
|
||||
"Advance Payment Ledger Entry",
|
||||
"Unreconcile Payment",
|
||||
"Unreconcile Payment Entries",
|
||||
)
|
||||
|
||||
@@ -397,7 +397,6 @@ class AccountsController(TransactionBase):
|
||||
def on_trash(self):
|
||||
from erpnext.accounts.utils import delete_exchange_gain_loss_journal
|
||||
|
||||
self._remove_advance_payment_ledger_entries()
|
||||
self._remove_references_in_repost_doctypes()
|
||||
self._remove_references_in_unreconcile()
|
||||
self.remove_serial_and_batch_bundle()
|
||||
@@ -426,6 +425,8 @@ class AccountsController(TransactionBase):
|
||||
(sle.voucher_type == self.doctype) & (sle.voucher_no == self.name)
|
||||
).run()
|
||||
|
||||
self._remove_advance_payment_ledger_entries()
|
||||
|
||||
def remove_serial_and_batch_bundle(self):
|
||||
bundles = frappe.get_all(
|
||||
"Serial and Batch Bundle",
|
||||
@@ -2233,54 +2234,30 @@ class AccountsController(TransactionBase):
|
||||
|
||||
def calculate_total_advance_from_ledger(self):
|
||||
adv = frappe.qb.DocType("Advance Payment Ledger Entry")
|
||||
advance = (
|
||||
frappe.qb.from_(adv)
|
||||
.select(adv.currency.as_("account_currency"), Abs(Sum(adv.amount)).as_("amount"))
|
||||
.where(
|
||||
(adv.against_voucher_type == self.doctype)
|
||||
& (adv.against_voucher_no == self.name)
|
||||
& (adv.company == self.company)
|
||||
)
|
||||
return (
|
||||
qb.from_(adv)
|
||||
.select(Abs(Sum(adv.amount)).as_("amount"), adv.currency.as_("account_currency"))
|
||||
.where(adv.company == self.company)
|
||||
.where(adv.delinked == 0)
|
||||
.where(adv.against_voucher_type == self.doctype)
|
||||
.where(adv.against_voucher_no == self.name)
|
||||
.run(as_dict=True)
|
||||
)
|
||||
return advance
|
||||
|
||||
def set_total_advance_paid(self):
|
||||
advance = self.calculate_total_advance_from_ledger()
|
||||
advance_paid, order_total = None, None
|
||||
advance_paid = 0
|
||||
|
||||
if advance:
|
||||
advance = advance[0]
|
||||
|
||||
advance_paid = flt(advance.amount, self.precision("advance_paid"))
|
||||
formatted_advance_paid = fmt_money(
|
||||
advance_paid, precision=self.precision("advance_paid"), currency=advance.account_currency
|
||||
)
|
||||
|
||||
if advance.account_currency:
|
||||
frappe.db.set_value(
|
||||
self.doctype, self.name, "party_account_currency", advance.account_currency
|
||||
)
|
||||
|
||||
if advance.account_currency == self.currency:
|
||||
order_total = self.get("rounded_total") or self.grand_total
|
||||
precision = "rounded_total" if self.get("rounded_total") else "grand_total"
|
||||
else:
|
||||
order_total = self.get("base_rounded_total") or self.base_grand_total
|
||||
precision = "base_rounded_total" if self.get("base_rounded_total") else "base_grand_total"
|
||||
|
||||
formatted_order_total = fmt_money(
|
||||
order_total, precision=self.precision(precision), currency=advance.account_currency
|
||||
)
|
||||
|
||||
if self.currency == self.company_currency and advance_paid > order_total:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Total advance ({0}) against Order {1} cannot be greater than the Grand Total ({2})"
|
||||
).format(formatted_advance_paid, self.name, formatted_order_total)
|
||||
)
|
||||
|
||||
self.db_set("advance_paid", advance_paid)
|
||||
self.db_set("advance_paid", advance_paid)
|
||||
|
||||
@property
|
||||
def company_abbr(self):
|
||||
@@ -2924,64 +2901,6 @@ class AccountsController(TransactionBase):
|
||||
def get_advance_payment_doctypes(self) -> list:
|
||||
return _get_advance_payment_doctypes()
|
||||
|
||||
def make_advance_payment_ledger_for_journal(self):
|
||||
advance_payment_doctypes = self.get_advance_payment_doctypes()
|
||||
advance_doctype_references = [
|
||||
x for x in self.accounts if x.reference_type in advance_payment_doctypes
|
||||
]
|
||||
|
||||
for x in advance_doctype_references:
|
||||
# Looking for payments
|
||||
dr_or_cr = (
|
||||
"credit_in_account_currency"
|
||||
if x.account_type == "Receivable"
|
||||
else "debit_in_account_currency"
|
||||
)
|
||||
|
||||
amount = x.get(dr_or_cr)
|
||||
if amount > 0:
|
||||
doc = frappe.new_doc("Advance Payment Ledger Entry")
|
||||
doc.company = self.company
|
||||
doc.voucher_type = self.doctype
|
||||
doc.voucher_no = self.name
|
||||
doc.against_voucher_type = x.reference_type
|
||||
doc.against_voucher_no = x.reference_name
|
||||
doc.amount = amount if self.docstatus == 1 else -1 * amount
|
||||
doc.event = "Submit" if self.docstatus == 1 else "Cancel"
|
||||
doc.currency = x.account_currency
|
||||
doc.flags.ignore_permissions = 1
|
||||
doc.save()
|
||||
|
||||
def make_advance_payment_ledger_for_payment(self):
|
||||
advance_payment_doctypes = self.get_advance_payment_doctypes()
|
||||
advance_doctype_references = [
|
||||
x for x in self.references if x.reference_doctype in advance_payment_doctypes
|
||||
]
|
||||
currency = (
|
||||
self.paid_from_account_currency
|
||||
if self.payment_type == "Receive"
|
||||
else self.paid_to_account_currency
|
||||
)
|
||||
for x in advance_doctype_references:
|
||||
doc = frappe.new_doc("Advance Payment Ledger Entry")
|
||||
doc.company = self.company
|
||||
doc.voucher_type = self.doctype
|
||||
doc.voucher_no = self.name
|
||||
doc.against_voucher_type = x.reference_doctype
|
||||
doc.against_voucher_no = x.reference_name
|
||||
doc.amount = x.allocated_amount if self.docstatus == 1 else -1 * x.allocated_amount
|
||||
doc.currency = currency
|
||||
doc.event = "Submit" if self.docstatus == 1 else "Cancel"
|
||||
doc.flags.ignore_permissions = 1
|
||||
doc.save()
|
||||
|
||||
def make_advance_payment_ledger_entries(self):
|
||||
if self.docstatus != 0:
|
||||
if self.doctype == "Journal Entry":
|
||||
self.make_advance_payment_ledger_for_journal()
|
||||
elif self.doctype == "Payment Entry":
|
||||
self.make_advance_payment_ledger_for_payment()
|
||||
|
||||
def set_transaction_currency_and_rate_in_gl_map(self, gl_entries):
|
||||
for x in gl_entries:
|
||||
x["transaction_currency"] = self.currency
|
||||
|
||||
@@ -361,7 +361,7 @@ erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes
|
||||
erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22
|
||||
erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request
|
||||
erpnext.patches.v14_0.update_pos_return_ledger_entries #2024-08-16
|
||||
erpnext.patches.v15_0.create_advance_payment_ledger_records
|
||||
erpnext.patches.v15_0.create_advance_payment_ledger_records #2025-07-04
|
||||
# below migration patch should always run last
|
||||
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
|
||||
erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20
|
||||
@@ -412,6 +412,7 @@ erpnext.patches.v15_0.drop_sle_indexes
|
||||
erpnext.patches.v15_0.update_pick_list_fields
|
||||
erpnext.patches.v15_0.update_pegged_currencies
|
||||
erpnext.patches.v15_0.set_company_on_pos_inv_merge_log
|
||||
erpnext.patches.v15_0.update_payment_ledger_entries_against_advance_doctypes
|
||||
erpnext.patches.v15_0.rename_price_list_to_buying_price_list
|
||||
erpnext.patches.v15_0.patch_missing_buying_price_list_in_material_request
|
||||
erpnext.patches.v15_0.remove_sales_partner_from_consolidated_sales_invoice
|
||||
|
||||
@@ -1,63 +1,28 @@
|
||||
import frappe
|
||||
from frappe import qb
|
||||
from frappe.query_builder.custom import ConstantColumn
|
||||
from frappe.model.naming import _generate_random_string
|
||||
from frappe.query_builder import Case
|
||||
from frappe.utils import now_datetime
|
||||
|
||||
from erpnext.accounts.utils import get_advance_payment_doctypes
|
||||
|
||||
def get_advance_doctypes() -> list:
|
||||
return frappe.get_hooks("advance_payment_doctypes")
|
||||
DOCTYPE = "Advance Payment Ledger Entry"
|
||||
|
||||
|
||||
def get_payments_with_so_po_reference() -> list:
|
||||
advance_payment_entries = []
|
||||
advance_doctypes = get_advance_doctypes()
|
||||
per = qb.DocType("Payment Entry Reference")
|
||||
payments_with_reference = (
|
||||
qb.from_(per)
|
||||
.select(per.parent)
|
||||
.distinct()
|
||||
.where(per.reference_doctype.isin(advance_doctypes) & per.docstatus.eq(1))
|
||||
.run()
|
||||
)
|
||||
if payments_with_reference:
|
||||
pe = qb.DocType("Payment Entry")
|
||||
advance_payment_entries = (
|
||||
qb.from_(pe)
|
||||
.select(ConstantColumn("Payment Entry").as_("doctype"))
|
||||
.select(pe.name)
|
||||
.where(pe.name.isin(payments_with_reference) & pe.docstatus.eq(1))
|
||||
.run(as_dict=True)
|
||||
)
|
||||
|
||||
return advance_payment_entries
|
||||
|
||||
|
||||
def get_journals_with_so_po_reference() -> list:
|
||||
advance_journal_entries = []
|
||||
advance_doctypes = get_advance_doctypes()
|
||||
jea = qb.DocType("Journal Entry Account")
|
||||
journals_with_reference = (
|
||||
qb.from_(jea)
|
||||
.select(jea.parent)
|
||||
.distinct()
|
||||
.where(jea.reference_type.isin(advance_doctypes) & jea.docstatus.eq(1))
|
||||
.run()
|
||||
)
|
||||
if journals_with_reference:
|
||||
je = qb.DocType("Journal Entry")
|
||||
advance_journal_entries = (
|
||||
qb.from_(je)
|
||||
.select(ConstantColumn("Journal Entry").as_("doctype"))
|
||||
.select(je.name)
|
||||
.where(je.name.isin(journals_with_reference) & je.docstatus.eq(1))
|
||||
.run(as_dict=True)
|
||||
)
|
||||
|
||||
return advance_journal_entries
|
||||
|
||||
|
||||
def make_advance_ledger_entries(vouchers: list):
|
||||
for x in vouchers:
|
||||
frappe.get_doc(x.doctype, x.name).make_advance_payment_ledger_entries()
|
||||
FIELDS = [
|
||||
"name",
|
||||
"creation",
|
||||
"modified",
|
||||
"owner",
|
||||
"modified_by",
|
||||
"company",
|
||||
"voucher_type",
|
||||
"voucher_no",
|
||||
"against_voucher_type",
|
||||
"against_voucher_no",
|
||||
"amount",
|
||||
"currency",
|
||||
"event",
|
||||
"delinked",
|
||||
]
|
||||
|
||||
|
||||
def execute():
|
||||
@@ -65,9 +30,102 @@ def execute():
|
||||
Description:
|
||||
Create Advance Payment Ledger Entry for all Payments made against Sales / Purchase Orders
|
||||
"""
|
||||
frappe.db.truncate("Advance Payment Ledger Entry")
|
||||
payment_entries = get_payments_with_so_po_reference()
|
||||
make_advance_ledger_entries(payment_entries)
|
||||
frappe.db.truncate(DOCTYPE)
|
||||
advance_doctpyes = get_advance_payment_doctypes()
|
||||
|
||||
journals = get_journals_with_so_po_reference()
|
||||
make_advance_ledger_entries(journals)
|
||||
if not advance_doctpyes:
|
||||
return
|
||||
|
||||
make_advance_ledger_entries_for_payment_entries(advance_doctpyes)
|
||||
make_advance_ledger_entries_for_journal_entries(advance_doctpyes)
|
||||
|
||||
|
||||
def make_advance_ledger_entries_for_payment_entries(advance_doctpyes) -> list:
|
||||
pe = frappe.qb.DocType("Payment Entry")
|
||||
per = frappe.qb.DocType("Payment Entry Reference")
|
||||
|
||||
entries = (
|
||||
frappe.qb.from_(per)
|
||||
.inner_join(pe)
|
||||
.on(pe.name == per.parent)
|
||||
.select(
|
||||
pe.company,
|
||||
per.parenttype.as_("voucher_type"),
|
||||
per.parent.as_("voucher_no"),
|
||||
per.reference_doctype.as_("against_voucher_type"),
|
||||
per.reference_name.as_("against_voucher_no"),
|
||||
per.allocated_amount.as_("amount"),
|
||||
Case()
|
||||
.when(pe.payment_type == "Receive", pe.paid_from_account_currency)
|
||||
.else_(pe.paid_to_account_currency)
|
||||
.as_("currency"),
|
||||
)
|
||||
.where(per.reference_doctype.isin(advance_doctpyes) & per.docstatus.eq(1))
|
||||
.run(as_dict=True)
|
||||
)
|
||||
|
||||
if not entries:
|
||||
return
|
||||
|
||||
bulk_insert_advance_entries(entries)
|
||||
|
||||
|
||||
def make_advance_ledger_entries_for_journal_entries(advance_doctpyes) -> list:
|
||||
je = frappe.qb.DocType("Journal Entry")
|
||||
jea = frappe.qb.DocType("Journal Entry Account")
|
||||
|
||||
entries = (
|
||||
frappe.qb.from_(jea)
|
||||
.inner_join(je)
|
||||
.on(je.name == jea.parent)
|
||||
.select(
|
||||
je.company,
|
||||
jea.parenttype.as_("voucher_type"),
|
||||
jea.parent.as_("voucher_no"),
|
||||
jea.reference_type.as_("against_voucher_type"),
|
||||
jea.reference_name.as_("against_voucher_no"),
|
||||
Case()
|
||||
.when(jea.account_type == "Receivable", jea.credit_in_account_currency)
|
||||
.else_(jea.debit_in_account_currency)
|
||||
.as_("amount"),
|
||||
jea.account_currency.as_("currency"),
|
||||
)
|
||||
.where(jea.reference_type.isin(advance_doctpyes) & jea.docstatus.eq(1))
|
||||
.run(as_dict=True)
|
||||
)
|
||||
|
||||
if not entries:
|
||||
return
|
||||
|
||||
bulk_insert_advance_entries(entries)
|
||||
|
||||
|
||||
def bulk_insert_advance_entries(entries):
|
||||
details = []
|
||||
user = frappe.session.user
|
||||
now = now_datetime()
|
||||
for entry in entries:
|
||||
if entry.amount < 0:
|
||||
continue
|
||||
details.append(get_values(user, now, entry))
|
||||
|
||||
frappe.db.bulk_insert(DOCTYPE, fields=FIELDS, values=details)
|
||||
|
||||
|
||||
def get_values(user, now, entry):
|
||||
return (
|
||||
_generate_random_string(10),
|
||||
now,
|
||||
now,
|
||||
user,
|
||||
user,
|
||||
entry.company,
|
||||
entry.voucher_type,
|
||||
entry.voucher_no,
|
||||
entry.against_voucher_type,
|
||||
entry.against_voucher_no,
|
||||
entry.amount * -1,
|
||||
entry.currency,
|
||||
"Submit",
|
||||
0,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import frappe
|
||||
|
||||
from erpnext.accounts.utils import get_advance_payment_doctypes
|
||||
|
||||
DOCTYPE = "Payment Ledger Entry"
|
||||
|
||||
|
||||
def execute():
|
||||
"""
|
||||
Description:
|
||||
Set against_voucher as entry for Payment Ledger Entry against advance vouchers.
|
||||
"""
|
||||
advance_payment_doctypes = get_advance_payment_doctypes()
|
||||
|
||||
if not advance_payment_doctypes:
|
||||
return
|
||||
ple = frappe.qb.DocType(DOCTYPE)
|
||||
|
||||
(
|
||||
frappe.qb.update(ple)
|
||||
.set(ple.against_voucher_type, ple.voucher_type)
|
||||
.set(ple.against_voucher_no, ple.voucher_no)
|
||||
.where(ple.against_voucher_type.isin(advance_payment_doctypes))
|
||||
.run()
|
||||
)
|
||||
@@ -42,8 +42,8 @@ erpnext.accounts.unreconcile_payment = {
|
||||
selection_map = selections.map(function (elem) {
|
||||
return {
|
||||
company: elem.company,
|
||||
voucher_type: elem.voucher_type,
|
||||
voucher_no: elem.voucher_no,
|
||||
voucher_type: elem.reference_doctype,
|
||||
voucher_no: elem.reference_name,
|
||||
against_voucher_type: frm.doc.doctype,
|
||||
against_voucher_no: frm.doc.name,
|
||||
};
|
||||
@@ -54,8 +54,8 @@ erpnext.accounts.unreconcile_payment = {
|
||||
company: elem.company,
|
||||
voucher_type: frm.doc.doctype,
|
||||
voucher_no: frm.doc.name,
|
||||
against_voucher_type: elem.voucher_type,
|
||||
against_voucher_no: elem.voucher_no,
|
||||
against_voucher_type: elem.reference_doctype,
|
||||
against_voucher_no: elem.reference_name,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -69,7 +69,7 @@ erpnext.accounts.unreconcile_payment = {
|
||||
let child_table_fields = [
|
||||
{
|
||||
label: __("Voucher Type"),
|
||||
fieldname: "voucher_type",
|
||||
fieldname: "reference_doctype",
|
||||
fieldtype: "Link",
|
||||
options: "DocType",
|
||||
in_list_view: 1,
|
||||
@@ -77,9 +77,9 @@ erpnext.accounts.unreconcile_payment = {
|
||||
},
|
||||
{
|
||||
label: __("Voucher No"),
|
||||
fieldname: "voucher_no",
|
||||
fieldname: "reference_name",
|
||||
fieldtype: "Dynamic Link",
|
||||
options: "voucher_type",
|
||||
options: "reference_doctype",
|
||||
in_list_view: 1,
|
||||
read_only: 1,
|
||||
},
|
||||
|
||||
@@ -444,6 +444,7 @@ class SalesOrder(SellingController):
|
||||
"GL Entry",
|
||||
"Stock Ledger Entry",
|
||||
"Payment Ledger Entry",
|
||||
"Advance Payment Ledger Entry",
|
||||
"Unreconcile Payment",
|
||||
"Unreconcile Payment Entries",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user