Merge pull request #38484 from frappe/mergify/bp/version-15-hotfix/pr-38393
refactor: GL entries build logic for `Advance in Separate Party Account` option. (backport #38393)
This commit is contained in:
commit
bf44e9ed64
@ -1055,6 +1055,28 @@ class PaymentEntry(AccountsController):
|
|||||||
item=self,
|
item=self,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
|
||||||
|
if self.book_advance_payments_in_separate_party_account:
|
||||||
|
gle = party_gl_dict.copy()
|
||||||
|
|
||||||
|
if self.payment_type == "Receive":
|
||||||
|
amount = self.base_paid_amount
|
||||||
|
else:
|
||||||
|
amount = self.base_received_amount
|
||||||
|
|
||||||
|
exchange_rate = self.get_exchange_rate()
|
||||||
|
amount_in_account_currency = amount * exchange_rate
|
||||||
|
gle.update(
|
||||||
|
{
|
||||||
|
dr_or_cr: amount,
|
||||||
|
dr_or_cr + "_in_account_currency": amount_in_account_currency,
|
||||||
|
"against_voucher_type": "Payment Entry",
|
||||||
|
"against_voucher": self.name,
|
||||||
|
"cost_center": self.cost_center,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
gl_entries.append(gle)
|
||||||
|
else:
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
# re-defining dr_or_cr for every reference in order to avoid the last value affecting calculation of reverse
|
# re-defining dr_or_cr for every reference in order to avoid the last value affecting calculation of reverse
|
||||||
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
|
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
|
||||||
@ -1065,15 +1087,8 @@ class PaymentEntry(AccountsController):
|
|||||||
gle = party_gl_dict.copy()
|
gle = party_gl_dict.copy()
|
||||||
|
|
||||||
allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d)
|
allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d)
|
||||||
|
|
||||||
if self.book_advance_payments_in_separate_party_account:
|
|
||||||
against_voucher_type = "Payment Entry"
|
|
||||||
against_voucher = self.name
|
|
||||||
else:
|
|
||||||
against_voucher_type = d.reference_doctype
|
|
||||||
against_voucher = d.reference_name
|
|
||||||
|
|
||||||
reverse_dr_or_cr = 0
|
reverse_dr_or_cr = 0
|
||||||
|
|
||||||
if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]:
|
if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||||
is_return = frappe.db.get_value(d.reference_doctype, d.reference_name, "is_return")
|
is_return = frappe.db.get_value(d.reference_doctype, d.reference_name, "is_return")
|
||||||
payable_party_types = get_party_types_from_account_type("Payable")
|
payable_party_types = get_party_types_from_account_type("Payable")
|
||||||
@ -1092,15 +1107,15 @@ class PaymentEntry(AccountsController):
|
|||||||
{
|
{
|
||||||
dr_or_cr: abs(allocated_amount_in_company_currency),
|
dr_or_cr: abs(allocated_amount_in_company_currency),
|
||||||
dr_or_cr + "_in_account_currency": abs(d.allocated_amount),
|
dr_or_cr + "_in_account_currency": abs(d.allocated_amount),
|
||||||
"against_voucher_type": against_voucher_type,
|
"against_voucher_type": d.reference_doctype,
|
||||||
"against_voucher": against_voucher,
|
"against_voucher": d.reference_name,
|
||||||
"cost_center": cost_center,
|
"cost_center": cost_center,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
gl_entries.append(gle)
|
gl_entries.append(gle)
|
||||||
|
|
||||||
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
|
|
||||||
if self.unallocated_amount:
|
if self.unallocated_amount:
|
||||||
|
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
|
||||||
exchange_rate = self.get_exchange_rate()
|
exchange_rate = self.get_exchange_rate()
|
||||||
base_unallocated_amount = self.unallocated_amount * exchange_rate
|
base_unallocated_amount = self.unallocated_amount * exchange_rate
|
||||||
|
|
||||||
@ -1114,53 +1129,31 @@ class PaymentEntry(AccountsController):
|
|||||||
|
|
||||||
gl_entries.append(gle)
|
gl_entries.append(gle)
|
||||||
|
|
||||||
def make_advance_gl_entries(self, against_voucher_type=None, against_voucher=None, cancel=0):
|
def make_advance_gl_entries(
|
||||||
if self.book_advance_payments_in_separate_party_account:
|
self, entry: object | dict = None, cancel: bool = 0, update_outstanding: str = "Yes"
|
||||||
gl_entries = []
|
|
||||||
for d in self.get("references"):
|
|
||||||
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Journal Entry"):
|
|
||||||
if not (against_voucher_type and against_voucher) or (
|
|
||||||
d.reference_doctype == against_voucher_type and d.reference_name == against_voucher
|
|
||||||
):
|
):
|
||||||
self.make_invoice_liability_entry(gl_entries, d)
|
gl_entries = []
|
||||||
|
self.add_advance_gl_entries(gl_entries, entry)
|
||||||
|
|
||||||
if cancel:
|
if cancel:
|
||||||
for entry in gl_entries:
|
make_reverse_gl_entries(gl_entries, partial_cancel=True)
|
||||||
frappe.db.set_value(
|
else:
|
||||||
"GL Entry",
|
make_gl_entries(gl_entries, update_outstanding=update_outstanding)
|
||||||
{
|
|
||||||
"voucher_no": self.name,
|
|
||||||
"voucher_type": self.doctype,
|
|
||||||
"voucher_detail_no": entry.voucher_detail_no,
|
|
||||||
"against_voucher_type": entry.against_voucher_type,
|
|
||||||
"against_voucher": entry.against_voucher,
|
|
||||||
},
|
|
||||||
"is_cancelled",
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
|
|
||||||
make_reverse_gl_entries(gl_entries=gl_entries, partial_cancel=True)
|
def add_advance_gl_entries(self, gl_entries: list, entry: object | dict | None):
|
||||||
return
|
"""
|
||||||
|
If 'entry' is passed, GL enties only for that reference is added.
|
||||||
|
"""
|
||||||
|
if self.book_advance_payments_in_separate_party_account:
|
||||||
|
references = [x for x in self.get("references")]
|
||||||
|
if entry:
|
||||||
|
references = [x for x in self.get("references") if x.name == entry.name]
|
||||||
|
|
||||||
# same reference added to payment entry
|
for ref in references:
|
||||||
for gl_entry in gl_entries.copy():
|
if ref.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Journal Entry"):
|
||||||
if frappe.db.exists(
|
self.add_advance_gl_for_reference(gl_entries, ref)
|
||||||
"GL Entry",
|
|
||||||
{
|
|
||||||
"account": gl_entry.account,
|
|
||||||
"voucher_type": gl_entry.voucher_type,
|
|
||||||
"voucher_no": gl_entry.voucher_no,
|
|
||||||
"voucher_detail_no": gl_entry.voucher_detail_no,
|
|
||||||
"debit": gl_entry.debit,
|
|
||||||
"credit": gl_entry.credit,
|
|
||||||
"is_cancelled": 0,
|
|
||||||
},
|
|
||||||
):
|
|
||||||
gl_entries.remove(gl_entry)
|
|
||||||
|
|
||||||
make_gl_entries(gl_entries)
|
def add_advance_gl_for_reference(self, gl_entries, invoice):
|
||||||
|
|
||||||
def make_invoice_liability_entry(self, gl_entries, invoice):
|
|
||||||
args_dict = {
|
args_dict = {
|
||||||
"party_type": self.party_type,
|
"party_type": self.party_type,
|
||||||
"party": self.party,
|
"party": self.party,
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from frappe import qb
|
|||||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||||
from frappe.utils import flt, nowdate
|
from frappe.utils import flt, nowdate
|
||||||
|
|
||||||
|
from erpnext.accounts.doctype.account.test_account import create_account
|
||||||
from erpnext.accounts.doctype.payment_entry.payment_entry import (
|
from erpnext.accounts.doctype.payment_entry.payment_entry import (
|
||||||
InvalidPaymentEntry,
|
InvalidPaymentEntry,
|
||||||
get_payment_entry,
|
get_payment_entry,
|
||||||
@ -1318,6 +1319,142 @@ class TestPaymentEntry(FrappeTestCase):
|
|||||||
]
|
]
|
||||||
self.check_gl_entries()
|
self.check_gl_entries()
|
||||||
|
|
||||||
|
def test_ledger_entries_for_advance_as_liability(self):
|
||||||
|
from erpnext.accounts.doctype.account.test_account import create_account
|
||||||
|
|
||||||
|
company = "_Test Company"
|
||||||
|
|
||||||
|
advance_account = create_account(
|
||||||
|
parent_account="Current Assets - _TC",
|
||||||
|
account_name="Advances Received",
|
||||||
|
company=company,
|
||||||
|
account_type="Receivable",
|
||||||
|
)
|
||||||
|
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Company",
|
||||||
|
company,
|
||||||
|
{
|
||||||
|
"book_advance_payments_in_separate_party_account": 1,
|
||||||
|
"default_advance_received_account": advance_account,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
# Advance Payment
|
||||||
|
pe = create_payment_entry(
|
||||||
|
party_type="Customer",
|
||||||
|
party="_Test Customer",
|
||||||
|
payment_type="Receive",
|
||||||
|
paid_from="Debtors - _TC",
|
||||||
|
paid_to="_Test Cash - _TC",
|
||||||
|
)
|
||||||
|
pe.save() # use save() to trigger set_liability_account()
|
||||||
|
pe.submit()
|
||||||
|
|
||||||
|
# Normal Invoice
|
||||||
|
si = create_sales_invoice(qty=10, rate=100, customer="_Test Customer")
|
||||||
|
|
||||||
|
pre_reconciliation_gle = [
|
||||||
|
{"account": advance_account, "debit": 0.0, "credit": 1000.0},
|
||||||
|
{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
|
||||||
|
]
|
||||||
|
pre_reconciliation_ple = [
|
||||||
|
{
|
||||||
|
"account": advance_account,
|
||||||
|
"voucher_no": pe.name,
|
||||||
|
"against_voucher_no": pe.name,
|
||||||
|
"amount": -1000.0,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
self.voucher_no = pe.name
|
||||||
|
self.expected_gle = pre_reconciliation_gle
|
||||||
|
self.expected_ple = pre_reconciliation_ple
|
||||||
|
self.check_gl_entries()
|
||||||
|
self.check_pl_entries()
|
||||||
|
|
||||||
|
# Partially reconcile advance against invoice
|
||||||
|
pr = frappe.get_doc("Payment Reconciliation")
|
||||||
|
pr.company = company
|
||||||
|
pr.party_type = "Customer"
|
||||||
|
pr.party = "_Test Customer"
|
||||||
|
pr.receivable_payable_account = si.debit_to
|
||||||
|
pr.default_advance_account = advance_account
|
||||||
|
pr.payment_name = pe.name
|
||||||
|
pr.invoice_name = si.name
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
|
||||||
|
self.assertEqual(len(pr.invoices), 1)
|
||||||
|
self.assertEqual(len(pr.payments), 1)
|
||||||
|
|
||||||
|
invoices = [x.as_dict() for x in pr.get("invoices")]
|
||||||
|
payments = [x.as_dict() for x in pr.get("payments")]
|
||||||
|
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||||
|
pr.allocation[0].allocated_amount = 400
|
||||||
|
pr.reconcile()
|
||||||
|
|
||||||
|
# assert General and Payment Ledger entries post partial reconciliation
|
||||||
|
self.expected_gle = [
|
||||||
|
{"account": si.debit_to, "debit": 0.0, "credit": 400.0},
|
||||||
|
{"account": advance_account, "debit": 400.0, "credit": 0.0},
|
||||||
|
{"account": advance_account, "debit": 0.0, "credit": 1000.0},
|
||||||
|
{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
|
||||||
|
]
|
||||||
|
self.expected_ple = [
|
||||||
|
{
|
||||||
|
"account": advance_account,
|
||||||
|
"voucher_no": pe.name,
|
||||||
|
"against_voucher_no": pe.name,
|
||||||
|
"amount": -1000.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"account": si.debit_to,
|
||||||
|
"voucher_no": pe.name,
|
||||||
|
"against_voucher_no": si.name,
|
||||||
|
"amount": -400.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"account": advance_account,
|
||||||
|
"voucher_no": pe.name,
|
||||||
|
"against_voucher_no": pe.name,
|
||||||
|
"amount": 400.0,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
self.check_gl_entries()
|
||||||
|
self.check_pl_entries()
|
||||||
|
|
||||||
|
# Unreconcile
|
||||||
|
unrecon = (
|
||||||
|
frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Unreconcile Payment",
|
||||||
|
"company": company,
|
||||||
|
"voucher_type": pe.doctype,
|
||||||
|
"voucher_no": pe.name,
|
||||||
|
"allocations": [{"reference_doctype": si.doctype, "reference_name": si.name}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.save()
|
||||||
|
.submit()
|
||||||
|
)
|
||||||
|
|
||||||
|
self.voucher_no = pe.name
|
||||||
|
self.expected_gle = pre_reconciliation_gle
|
||||||
|
self.expected_ple = pre_reconciliation_ple
|
||||||
|
self.check_gl_entries()
|
||||||
|
self.check_pl_entries()
|
||||||
|
|
||||||
|
def check_pl_entries(self):
|
||||||
|
ple = frappe.qb.DocType("Payment Ledger Entry")
|
||||||
|
pl_entries = (
|
||||||
|
frappe.qb.from_(ple)
|
||||||
|
.select(ple.account, ple.voucher_no, ple.against_voucher_no, ple.amount)
|
||||||
|
.where((ple.voucher_no == self.voucher_no) & (ple.delinked == 0))
|
||||||
|
.orderby(ple.creation)
|
||||||
|
).run(as_dict=True)
|
||||||
|
for row in range(len(self.expected_ple)):
|
||||||
|
for field in ["account", "voucher_no", "against_voucher_no", "amount"]:
|
||||||
|
self.assertEqual(self.expected_ple[row][field], pl_entries[row][field])
|
||||||
|
|
||||||
def check_gl_entries(self):
|
def check_gl_entries(self):
|
||||||
gle = frappe.qb.DocType("GL Entry")
|
gle = frappe.qb.DocType("GL Entry")
|
||||||
gl_entries = (
|
gl_entries = (
|
||||||
|
|||||||
@ -1747,6 +1747,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
|||||||
paid_to="Creditors - _TC",
|
paid_to="Creditors - _TC",
|
||||||
paid_amount=500,
|
paid_amount=500,
|
||||||
)
|
)
|
||||||
|
pe.save() # save trigger is needed for set_liability_account() to be executed
|
||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
pi = make_purchase_invoice(
|
pi = make_purchase_invoice(
|
||||||
@ -1769,10 +1770,10 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
|||||||
|
|
||||||
# Check GL Entry against payment doctype
|
# Check GL Entry against payment doctype
|
||||||
expected_gle = [
|
expected_gle = [
|
||||||
["Advances Paid - _TC", 0.0, 500, nowdate()],
|
["Advances Paid - _TC", 500.0, 0.0, nowdate()],
|
||||||
|
["Advances Paid - _TC", 0.0, 500.0, nowdate()],
|
||||||
["Cash - _TC", 0.0, 500, nowdate()],
|
["Cash - _TC", 0.0, 500, nowdate()],
|
||||||
["Creditors - _TC", 500, 0.0, nowdate()],
|
["Creditors - _TC", 500, 0.0, nowdate()],
|
||||||
["Creditors - _TC", 500, 0.0, nowdate()],
|
|
||||||
]
|
]
|
||||||
|
|
||||||
check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry")
|
check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry")
|
||||||
|
|||||||
@ -3377,21 +3377,21 @@ class TestSalesInvoice(FrappeTestCase):
|
|||||||
def test_advance_entries_as_liability(self):
|
def test_advance_entries_as_liability(self):
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
|
||||||
|
|
||||||
account = create_account(
|
advance_account = create_account(
|
||||||
parent_account="Current Liabilities - _TC",
|
parent_account="Current Liabilities - _TC",
|
||||||
account_name="Advances Received",
|
account_name="Advances Received",
|
||||||
company="_Test Company",
|
company="_Test Company",
|
||||||
account_type="Receivable",
|
account_type="Receivable",
|
||||||
)
|
)
|
||||||
|
|
||||||
set_advance_flag(company="_Test Company", flag=1, default_account=account)
|
set_advance_flag(company="_Test Company", flag=1, default_account=advance_account)
|
||||||
|
|
||||||
pe = create_payment_entry(
|
pe = create_payment_entry(
|
||||||
company="_Test Company",
|
company="_Test Company",
|
||||||
payment_type="Receive",
|
payment_type="Receive",
|
||||||
party_type="Customer",
|
party_type="Customer",
|
||||||
party="_Test Customer",
|
party="_Test Customer",
|
||||||
paid_from="Debtors - _TC",
|
paid_from=advance_account,
|
||||||
paid_to="Cash - _TC",
|
paid_to="Cash - _TC",
|
||||||
paid_amount=1000,
|
paid_amount=1000,
|
||||||
)
|
)
|
||||||
@ -3417,9 +3417,9 @@ class TestSalesInvoice(FrappeTestCase):
|
|||||||
|
|
||||||
# Check GL Entry against payment doctype
|
# Check GL Entry against payment doctype
|
||||||
expected_gle = [
|
expected_gle = [
|
||||||
|
["Advances Received - _TC", 0.0, 1000.0, nowdate()],
|
||||||
["Advances Received - _TC", 500, 0.0, nowdate()],
|
["Advances Received - _TC", 500, 0.0, nowdate()],
|
||||||
["Cash - _TC", 1000, 0.0, nowdate()],
|
["Cash - _TC", 1000, 0.0, nowdate()],
|
||||||
["Debtors - _TC", 0.0, 1000, nowdate()],
|
|
||||||
["Debtors - _TC", 0.0, 500, nowdate()],
|
["Debtors - _TC", 0.0, 500, nowdate()],
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3456,6 +3456,93 @@ class TestSalesInvoice(FrappeTestCase):
|
|||||||
si.items[0].rate = 10
|
si.items[0].rate = 10
|
||||||
si.save()
|
si.save()
|
||||||
|
|
||||||
|
def test_partial_allocation_on_advance_as_liability(self):
|
||||||
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
|
||||||
|
|
||||||
|
company = "_Test Company"
|
||||||
|
customer = "_Test Customer"
|
||||||
|
debtors_acc = "Debtors - _TC"
|
||||||
|
advance_account = create_account(
|
||||||
|
parent_account="Current Liabilities - _TC",
|
||||||
|
account_name="Advances Received",
|
||||||
|
company="_Test Company",
|
||||||
|
account_type="Receivable",
|
||||||
|
)
|
||||||
|
|
||||||
|
set_advance_flag(company="_Test Company", flag=1, default_account=advance_account)
|
||||||
|
|
||||||
|
pe = create_payment_entry(
|
||||||
|
company=company,
|
||||||
|
payment_type="Receive",
|
||||||
|
party_type="Customer",
|
||||||
|
party=customer,
|
||||||
|
paid_from=advance_account,
|
||||||
|
paid_to="Cash - _TC",
|
||||||
|
paid_amount=1000,
|
||||||
|
)
|
||||||
|
pe.submit()
|
||||||
|
|
||||||
|
si = create_sales_invoice(
|
||||||
|
company=company,
|
||||||
|
customer=customer,
|
||||||
|
do_not_save=True,
|
||||||
|
do_not_submit=True,
|
||||||
|
rate=1000,
|
||||||
|
price_list_rate=1000,
|
||||||
|
)
|
||||||
|
si.base_grand_total = 1000
|
||||||
|
si.grand_total = 1000
|
||||||
|
si.set_advances()
|
||||||
|
for advance in si.advances:
|
||||||
|
advance.allocated_amount = 200 if advance.reference_name == pe.name else 0
|
||||||
|
si.save()
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
self.assertEqual(si.advances[0].allocated_amount, 200)
|
||||||
|
|
||||||
|
# Check GL Entry against partial from advance
|
||||||
|
expected_gle = [
|
||||||
|
[advance_account, 0.0, 1000.0, nowdate()],
|
||||||
|
[advance_account, 200.0, 0.0, nowdate()],
|
||||||
|
["Cash - _TC", 1000.0, 0.0, nowdate()],
|
||||||
|
[debtors_acc, 0.0, 200.0, nowdate()],
|
||||||
|
]
|
||||||
|
check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry")
|
||||||
|
si.reload()
|
||||||
|
self.assertEqual(si.outstanding_amount, 800.0)
|
||||||
|
|
||||||
|
pr = frappe.get_doc("Payment Reconciliation")
|
||||||
|
pr.company = company
|
||||||
|
pr.party_type = "Customer"
|
||||||
|
pr.party = customer
|
||||||
|
pr.receivable_payable_account = debtors_acc
|
||||||
|
pr.default_advance_account = advance_account
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
|
||||||
|
# allocate some more of the same advance
|
||||||
|
# self.assertEqual(len(pr.invoices), 1)
|
||||||
|
# self.assertEqual(len(pr.payments), 1)
|
||||||
|
invoices = [x.as_dict() for x in pr.invoices if x.get("invoice_number") == si.name]
|
||||||
|
payments = [x.as_dict() for x in pr.payments if x.get("reference_name") == pe.name]
|
||||||
|
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||||
|
pr.allocation[0].allocated_amount = 300
|
||||||
|
pr.reconcile()
|
||||||
|
|
||||||
|
si.reload()
|
||||||
|
self.assertEqual(si.outstanding_amount, 500.0)
|
||||||
|
|
||||||
|
# Check GL Entry against multi partial allocations from advance
|
||||||
|
expected_gle = [
|
||||||
|
[advance_account, 0.0, 1000.0, nowdate()],
|
||||||
|
[advance_account, 200.0, 0.0, nowdate()],
|
||||||
|
[advance_account, 300.0, 0.0, nowdate()],
|
||||||
|
["Cash - _TC", 1000.0, 0.0, nowdate()],
|
||||||
|
[debtors_acc, 0.0, 200.0, nowdate()],
|
||||||
|
[debtors_acc, 0.0, 300.0, nowdate()],
|
||||||
|
]
|
||||||
|
check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry")
|
||||||
|
set_advance_flag(company="_Test Company", flag=0, default_account="")
|
||||||
|
|
||||||
|
|
||||||
def set_advance_flag(company, flag, default_account):
|
def set_advance_flag(company, flag, default_account):
|
||||||
frappe.db.set_value(
|
frappe.db.set_value(
|
||||||
|
|||||||
@ -597,7 +597,30 @@ def make_reverse_gl_entries(
|
|||||||
|
|
||||||
is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries)
|
is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries)
|
||||||
validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"])
|
validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"])
|
||||||
if not partial_cancel:
|
if partial_cancel:
|
||||||
|
# Partial cancel is only used by `Advance` in separate account feature.
|
||||||
|
# Only cancel GL entries for unlinked reference using `voucher_detail_no`
|
||||||
|
gle = frappe.qb.DocType("GL Entry")
|
||||||
|
for x in gl_entries:
|
||||||
|
query = (
|
||||||
|
frappe.qb.update(gle)
|
||||||
|
.set(gle.is_cancelled, True)
|
||||||
|
.set(gle.modified, now())
|
||||||
|
.set(gle.modified_by, frappe.session.user)
|
||||||
|
.where(
|
||||||
|
(gle.company == x.company)
|
||||||
|
& (gle.account == x.account)
|
||||||
|
& (gle.party_type == x.party_type)
|
||||||
|
& (gle.party == x.party)
|
||||||
|
& (gle.voucher_type == x.voucher_type)
|
||||||
|
& (gle.voucher_no == x.voucher_no)
|
||||||
|
& (gle.against_voucher_type == x.against_voucher_type)
|
||||||
|
& (gle.against_voucher == x.against_voucher)
|
||||||
|
& (gle.voucher_detail_no == x.voucher_detail_no)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
query.run()
|
||||||
|
else:
|
||||||
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"])
|
||||||
|
|
||||||
for entry in gl_entries:
|
for entry in gl_entries:
|
||||||
|
|||||||
@ -472,6 +472,10 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n
|
|||||||
# cancel advance entry
|
# cancel advance entry
|
||||||
doc = frappe.get_doc(voucher_type, voucher_no)
|
doc = frappe.get_doc(voucher_type, voucher_no)
|
||||||
frappe.flags.ignore_party_validation = True
|
frappe.flags.ignore_party_validation = True
|
||||||
|
|
||||||
|
# For payments with `Advance` in separate account feature enabled, only new ledger entries are posted for each reference.
|
||||||
|
# No need to cancel/delete payment ledger entries
|
||||||
|
if not (voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account):
|
||||||
_delete_pl_entries(voucher_type, voucher_no)
|
_delete_pl_entries(voucher_type, voucher_no)
|
||||||
|
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
@ -487,13 +491,18 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n
|
|||||||
entry.update({"referenced_row": referenced_row})
|
entry.update({"referenced_row": referenced_row})
|
||||||
doc.make_exchange_gain_loss_journal([entry])
|
doc.make_exchange_gain_loss_journal([entry])
|
||||||
else:
|
else:
|
||||||
update_reference_in_payment_entry(
|
referenced_row = update_reference_in_payment_entry(
|
||||||
entry, doc, do_not_save=True, skip_ref_details_update_for_pe=skip_ref_details_update_for_pe
|
entry, doc, do_not_save=True, skip_ref_details_update_for_pe=skip_ref_details_update_for_pe
|
||||||
)
|
)
|
||||||
|
|
||||||
doc.save(ignore_permissions=True)
|
doc.save(ignore_permissions=True)
|
||||||
# re-submit advance entry
|
# re-submit advance entry
|
||||||
doc = frappe.get_doc(entry.voucher_type, entry.voucher_no)
|
doc = frappe.get_doc(entry.voucher_type, entry.voucher_no)
|
||||||
|
|
||||||
|
if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account:
|
||||||
|
# both ledgers must be posted to for `Advance` in separate account feature
|
||||||
|
doc.make_advance_gl_entries(referenced_row, update_outstanding="No")
|
||||||
|
else:
|
||||||
gl_map = doc.build_gl_map()
|
gl_map = doc.build_gl_map()
|
||||||
create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1)
|
create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1)
|
||||||
|
|
||||||
@ -502,8 +511,6 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n
|
|||||||
update_voucher_outstanding(
|
update_voucher_outstanding(
|
||||||
entry.against_voucher_type, entry.against_voucher, entry.account, entry.party_type, entry.party
|
entry.against_voucher_type, entry.against_voucher, entry.account, entry.party_type, entry.party
|
||||||
)
|
)
|
||||||
if voucher_type == "Payment Entry":
|
|
||||||
doc.make_advance_gl_entries(entry.against_voucher_type, entry.against_voucher)
|
|
||||||
|
|
||||||
frappe.flags.ignore_party_validation = False
|
frappe.flags.ignore_party_validation = False
|
||||||
|
|
||||||
@ -671,11 +678,12 @@ def update_reference_in_payment_entry(
|
|||||||
new_row.docstatus = 1
|
new_row.docstatus = 1
|
||||||
for field in list(reference_details):
|
for field in list(reference_details):
|
||||||
new_row.set(field, reference_details[field])
|
new_row.set(field, reference_details[field])
|
||||||
|
row = new_row
|
||||||
else:
|
else:
|
||||||
new_row = payment_entry.append("references")
|
new_row = payment_entry.append("references")
|
||||||
new_row.docstatus = 1
|
new_row.docstatus = 1
|
||||||
new_row.update(reference_details)
|
new_row.update(reference_details)
|
||||||
|
row = new_row
|
||||||
|
|
||||||
payment_entry.flags.ignore_validate_update_after_submit = True
|
payment_entry.flags.ignore_validate_update_after_submit = True
|
||||||
payment_entry.clear_unallocated_reference_document_rows()
|
payment_entry.clear_unallocated_reference_document_rows()
|
||||||
@ -688,6 +696,7 @@ def update_reference_in_payment_entry(
|
|||||||
|
|
||||||
if not do_not_save:
|
if not do_not_save:
|
||||||
payment_entry.save(ignore_permissions=True)
|
payment_entry.save(ignore_permissions=True)
|
||||||
|
return row
|
||||||
|
|
||||||
|
|
||||||
def cancel_exchange_gain_loss_journal(
|
def cancel_exchange_gain_loss_journal(
|
||||||
@ -864,7 +873,13 @@ def remove_ref_doc_link_from_pe(
|
|||||||
try:
|
try:
|
||||||
pe_doc = frappe.get_doc("Payment Entry", pe)
|
pe_doc = frappe.get_doc("Payment Entry", pe)
|
||||||
pe_doc.set_amounts()
|
pe_doc.set_amounts()
|
||||||
pe_doc.make_advance_gl_entries(against_voucher_type=ref_type, against_voucher=ref_no, cancel=1)
|
|
||||||
|
# Call cancel on only removed reference
|
||||||
|
references = [
|
||||||
|
x for x in pe_doc.references if x.reference_doctype == ref_type and x.reference_name == ref_no
|
||||||
|
]
|
||||||
|
[pe_doc.make_advance_gl_entries(x, cancel=1) for x in references]
|
||||||
|
|
||||||
pe_doc.clear_unallocated_reference_document_rows()
|
pe_doc.clear_unallocated_reference_document_rows()
|
||||||
pe_doc.validate_payment_type_with_outstanding()
|
pe_doc.validate_payment_type_with_outstanding()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user