mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-13 11:55:11 +00:00
Merge pull request #43946 from frappe/mergify/bp/version-15-hotfix/pr-43709
feat: Ledger for advance payment (backport #43709)
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
// frappe.ui.form.on("Advance Payment Ledger Entry", {
|
||||
// refresh(frm) {
|
||||
|
||||
// },
|
||||
// });
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"creation": "2024-10-16 16:57:12.085072",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"company",
|
||||
"voucher_type",
|
||||
"voucher_no",
|
||||
"against_voucher_type",
|
||||
"against_voucher_no",
|
||||
"amount",
|
||||
"currency",
|
||||
"event"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "voucher_type",
|
||||
"fieldtype": "Link",
|
||||
"label": "Voucher Type",
|
||||
"options": "DocType",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "voucher_no",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Voucher No",
|
||||
"options": "voucher_type",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "against_voucher_type",
|
||||
"fieldtype": "Link",
|
||||
"label": "Against Voucher Type",
|
||||
"options": "DocType",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "against_voucher_no",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Against Voucher No",
|
||||
"options": "against_voucher_type",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Amount",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Currency",
|
||||
"options": "Currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "event",
|
||||
"fieldtype": "Data",
|
||||
"label": "Event",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2024-10-16 17:11:28.143979",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Advance Payment Ledger Entry",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class AdvancePaymentLedgerEntry(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
against_voucher_no: DF.DynamicLink | None
|
||||
against_voucher_type: DF.Link | None
|
||||
amount: DF.Currency
|
||||
company: DF.Link | None
|
||||
currency: DF.Link | None
|
||||
event: DF.Data | None
|
||||
voucher_no: DF.DynamicLink | None
|
||||
voucher_type: DF.Link | None
|
||||
# end: auto-generated types
|
||||
|
||||
pass
|
||||
@@ -0,0 +1,222 @@
|
||||
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.utils import nowdate, today
|
||||
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
||||
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
|
||||
|
||||
class TestAdvancePaymentLedgerEntry(AccountsTestMixin, FrappeTestCase):
|
||||
"""
|
||||
Integration tests for AdvancePaymentLedgerEntry.
|
||||
Use this class for testing interactions between multiple components.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.create_company()
|
||||
self.create_usd_receivable_account()
|
||||
self.create_usd_payable_account()
|
||||
self.create_item()
|
||||
self.clear_old_entries()
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.rollback()
|
||||
|
||||
def create_sales_order(self, qty=1, rate=100, currency="INR", do_not_submit=False):
|
||||
"""
|
||||
Helper method
|
||||
"""
|
||||
so = make_sales_order(
|
||||
company=self.company,
|
||||
customer=self.customer,
|
||||
currency=currency,
|
||||
item=self.item,
|
||||
qty=qty,
|
||||
rate=rate,
|
||||
transaction_date=today(),
|
||||
do_not_submit=do_not_submit,
|
||||
)
|
||||
return so
|
||||
|
||||
def create_purchase_order(self, qty=1, rate=100, currency="INR", do_not_submit=False):
|
||||
"""
|
||||
Helper method
|
||||
"""
|
||||
po = create_purchase_order(
|
||||
company=self.company,
|
||||
customer=self.supplier,
|
||||
currency=currency,
|
||||
item=self.item,
|
||||
qty=qty,
|
||||
rate=rate,
|
||||
transaction_date=today(),
|
||||
do_not_submit=do_not_submit,
|
||||
)
|
||||
return po
|
||||
|
||||
def test_so_advance_paid_and_currency_with_payment(self):
|
||||
self.create_customer("_Test USD Customer", "USD")
|
||||
|
||||
so = self.create_sales_order(currency="USD", do_not_submit=True)
|
||||
so.conversion_rate = 80
|
||||
so.submit()
|
||||
|
||||
pe_exchange_rate = 85
|
||||
pe = get_payment_entry(so.doctype, so.name, bank_account=self.cash)
|
||||
pe.reference_no = "1"
|
||||
pe.reference_date = nowdate()
|
||||
pe.paid_from = self.debtors_usd
|
||||
pe.paid_from_account_currency = "USD"
|
||||
pe.source_exchange_rate = pe_exchange_rate
|
||||
pe.paid_amount = so.grand_total
|
||||
pe.received_amount = pe_exchange_rate * pe.paid_amount
|
||||
pe.references[0].outstanding_amount = 100
|
||||
pe.references[0].total_amount = 100
|
||||
pe.references[0].allocated_amount = 100
|
||||
pe.save().submit()
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.advance_paid, 100)
|
||||
self.assertEqual(so.party_account_currency, "USD")
|
||||
|
||||
# cancel advance payment
|
||||
pe.reload()
|
||||
pe.cancel()
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.advance_paid, 0)
|
||||
self.assertEqual(so.party_account_currency, "USD")
|
||||
|
||||
def test_so_advance_paid_and_currency_with_journal(self):
|
||||
self.create_customer("_Test USD Customer", "USD")
|
||||
|
||||
so = self.create_sales_order(currency="USD", do_not_submit=True)
|
||||
so.conversion_rate = 80
|
||||
so.submit()
|
||||
|
||||
je_exchange_rate = 85
|
||||
je = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Journal Entry",
|
||||
"company": self.company,
|
||||
"voucher_type": "Journal Entry",
|
||||
"posting_date": so.transaction_date,
|
||||
"multi_currency": True,
|
||||
"accounts": [
|
||||
{
|
||||
"account": self.debtors_usd,
|
||||
"party_type": "Customer",
|
||||
"party": so.customer,
|
||||
"credit": 8500,
|
||||
"credit_in_account_currency": 100,
|
||||
"is_advance": "Yes",
|
||||
"reference_type": so.doctype,
|
||||
"reference_name": so.name,
|
||||
"exchange_rate": je_exchange_rate,
|
||||
},
|
||||
{
|
||||
"account": self.cash,
|
||||
"debit": 8500,
|
||||
"debit_in_account_currency": 8500,
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
je.save().submit()
|
||||
so.reload()
|
||||
self.assertEqual(so.advance_paid, 100)
|
||||
self.assertEqual(so.party_account_currency, "USD")
|
||||
|
||||
# cancel advance payment
|
||||
je.reload()
|
||||
je.cancel()
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.advance_paid, 0)
|
||||
self.assertEqual(so.party_account_currency, "USD")
|
||||
|
||||
def test_po_advance_paid_and_currency_with_payment(self):
|
||||
self.create_supplier("_Test USD Supplier", "USD")
|
||||
|
||||
po = self.create_purchase_order(currency="USD", do_not_submit=True)
|
||||
po.conversion_rate = 80
|
||||
po.submit()
|
||||
|
||||
pe_exchange_rate = 85
|
||||
pe = get_payment_entry(po.doctype, po.name, bank_account=self.cash)
|
||||
pe.reference_no = "1"
|
||||
pe.reference_date = nowdate()
|
||||
pe.paid_to = self.creditors_usd
|
||||
pe.paid_to_account_currency = "USD"
|
||||
pe.target_exchange_rate = pe_exchange_rate
|
||||
pe.received_amount = po.grand_total
|
||||
pe.paid_amount = pe_exchange_rate * pe.received_amount
|
||||
pe.references[0].outstanding_amount = 100
|
||||
pe.references[0].total_amount = 100
|
||||
pe.references[0].allocated_amount = 100
|
||||
pe.save().submit()
|
||||
|
||||
po.reload()
|
||||
self.assertEqual(po.advance_paid, 100)
|
||||
self.assertEqual(po.party_account_currency, "USD")
|
||||
|
||||
# cancel advance payment
|
||||
pe.reload()
|
||||
pe.cancel()
|
||||
|
||||
po.reload()
|
||||
self.assertEqual(po.advance_paid, 0)
|
||||
self.assertEqual(po.party_account_currency, "USD")
|
||||
|
||||
def test_po_advance_paid_and_currency_with_journal(self):
|
||||
self.create_supplier("_Test USD Supplier", "USD")
|
||||
|
||||
po = self.create_purchase_order(currency="USD", do_not_submit=True)
|
||||
po.conversion_rate = 80
|
||||
po.submit()
|
||||
|
||||
je_exchange_rate = 85
|
||||
je = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Journal Entry",
|
||||
"company": self.company,
|
||||
"voucher_type": "Journal Entry",
|
||||
"posting_date": po.transaction_date,
|
||||
"multi_currency": True,
|
||||
"accounts": [
|
||||
{
|
||||
"account": self.creditors_usd,
|
||||
"party_type": "Supplier",
|
||||
"party": po.supplier,
|
||||
"debit": 8500,
|
||||
"debit_in_account_currency": 100,
|
||||
"is_advance": "Yes",
|
||||
"reference_type": po.doctype,
|
||||
"reference_name": po.name,
|
||||
"exchange_rate": je_exchange_rate,
|
||||
},
|
||||
{
|
||||
"account": self.cash,
|
||||
"credit": 8500,
|
||||
"credit_in_account_currency": 8500,
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
je.save().submit()
|
||||
po.reload()
|
||||
self.assertEqual(po.advance_paid, 100)
|
||||
self.assertEqual(po.party_account_currency, "USD")
|
||||
|
||||
# cancel advance payment
|
||||
je.reload()
|
||||
je.cancel()
|
||||
|
||||
po.reload()
|
||||
self.assertEqual(po.advance_paid, 0)
|
||||
self.assertEqual(po.party_account_currency, "USD")
|
||||
@@ -188,6 +188,7 @@ 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()
|
||||
@@ -218,8 +219,10 @@ class JournalEntry(AccountsController):
|
||||
"Repost Accounting Ledger Items",
|
||||
"Unreconcile Payment",
|
||||
"Unreconcile Payment Entries",
|
||||
"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()
|
||||
|
||||
@@ -110,6 +110,7 @@ class PaymentEntry(AccountsController):
|
||||
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()
|
||||
|
||||
@@ -190,6 +191,7 @@ class PaymentEntry(AccountsController):
|
||||
"Repost Accounting Ledger Items",
|
||||
"Unreconcile Payment",
|
||||
"Unreconcile Payment Entries",
|
||||
"Advance Payment Ledger Entry",
|
||||
)
|
||||
super().on_cancel()
|
||||
self.make_gl_entries(cancel=1)
|
||||
@@ -197,6 +199,7 @@ class PaymentEntry(AccountsController):
|
||||
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()
|
||||
|
||||
|
||||
@@ -1995,7 +1995,7 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
|
||||
# Check if SO is unlinked/replaced by SI in PE & if SO advance paid is 0
|
||||
self.assertEqual(pe.references[0].reference_name, si.name)
|
||||
self.assertEqual(sales_order.advance_paid, 0.0)
|
||||
self.assertEqual(sales_order.advance_paid, 300.0)
|
||||
|
||||
# check outstanding after advance allocation
|
||||
self.assertEqual(
|
||||
|
||||
@@ -362,10 +362,14 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
|
||||
# Assert 'Advance Paid'
|
||||
so.reload()
|
||||
pe.reload()
|
||||
self.assertEqual(so.advance_paid, 0)
|
||||
self.assertEqual(so.advance_paid, 100)
|
||||
self.assertEqual(len(pe.references), 0)
|
||||
self.assertEqual(pe.unallocated_amount, 100)
|
||||
|
||||
pe.cancel()
|
||||
so.reload()
|
||||
self.assertEqual(so.advance_paid, 100)
|
||||
|
||||
def test_06_unreconcile_advance_from_payment_entry(self):
|
||||
self.enable_advance_as_liability()
|
||||
so1 = self.create_sales_order()
|
||||
@@ -411,7 +415,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
|
||||
so2.reload()
|
||||
pe.reload()
|
||||
self.assertEqual(so1.advance_paid, 150)
|
||||
self.assertEqual(so2.advance_paid, 0)
|
||||
self.assertEqual(so2.advance_paid, 110)
|
||||
self.assertEqual(len(pe.references), 1)
|
||||
self.assertEqual(pe.unallocated_amount, 110)
|
||||
|
||||
@@ -459,6 +463,6 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
|
||||
|
||||
# Assert 'Advance Paid'
|
||||
so.reload()
|
||||
self.assertEqual(so.advance_paid, 0)
|
||||
self.assertEqual(so.advance_paid, 1000)
|
||||
|
||||
self.disable_advance_as_liability()
|
||||
|
||||
@@ -345,9 +345,21 @@ class AccountsController(TransactionBase):
|
||||
repost_doc.flags.ignore_links = True
|
||||
repost_doc.save(ignore_permissions=True)
|
||||
|
||||
def _remove_advance_payment_ledger_entries(self):
|
||||
adv = qb.DocType("Advance Payment Ledger Entry")
|
||||
qb.from_(adv).delete().where(adv.voucher_type.eq(self.doctype) & adv.voucher_no.eq(self.name)).run()
|
||||
|
||||
advance_payment_doctypes = frappe.get_hooks("advance_payment_doctypes")
|
||||
|
||||
if self.doctype in advance_payment_doctypes:
|
||||
qb.from_(adv).delete().where(
|
||||
adv.against_voucher_type.eq(self.doctype) & adv.against_voucher_no.eq(self.name)
|
||||
).run()
|
||||
|
||||
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()
|
||||
@@ -1935,21 +1947,23 @@ class AccountsController(TransactionBase):
|
||||
|
||||
return stock_items
|
||||
|
||||
def set_total_advance_paid(self):
|
||||
ple = frappe.qb.DocType("Payment Ledger Entry")
|
||||
party = self.customer if self.doctype == "Sales Order" else self.supplier
|
||||
def calculate_total_advance_from_ledger(self):
|
||||
adv = frappe.qb.DocType("Advance Payment Ledger Entry")
|
||||
advance = (
|
||||
frappe.qb.from_(ple)
|
||||
.select(ple.account_currency, Abs(Sum(ple.amount_in_account_currency)).as_("amount"))
|
||||
frappe.qb.from_(adv)
|
||||
.select(adv.currency.as_("account_currency"), Abs(Sum(adv.amount)).as_("amount"))
|
||||
.where(
|
||||
(ple.against_voucher_type == self.doctype)
|
||||
& (ple.against_voucher_no == self.name)
|
||||
& (ple.party == party)
|
||||
& (ple.delinked == 0)
|
||||
& (ple.company == self.company)
|
||||
(adv.against_voucher_type == self.doctype)
|
||||
& (adv.against_voucher_no == self.name)
|
||||
& (adv.company == self.company)
|
||||
)
|
||||
.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
|
||||
|
||||
if advance:
|
||||
advance = advance[0]
|
||||
@@ -2546,6 +2560,65 @@ class AccountsController(TransactionBase):
|
||||
repost_ledger.insert()
|
||||
repost_ledger.submit()
|
||||
|
||||
def get_advance_payment_doctypes(self) -> list:
|
||||
return frappe.get_hooks("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.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.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()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_tax_rate(account_head):
|
||||
|
||||
@@ -359,6 +359,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
|
||||
# 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
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import frappe
|
||||
from frappe import qb
|
||||
from frappe.query_builder.custom import ConstantColumn
|
||||
|
||||
|
||||
def get_advance_doctypes() -> list:
|
||||
return frappe.get_hooks("advance_payment_doctypes")
|
||||
|
||||
|
||||
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()
|
||||
|
||||
|
||||
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)
|
||||
|
||||
journals = get_journals_with_so_po_reference()
|
||||
make_advance_ledger_entries(journals)
|
||||
Reference in New Issue
Block a user