mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 16:34:46 +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.validate_cheque_info()
|
||||||
self.check_credit_limit()
|
self.check_credit_limit()
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
self.make_advance_payment_ledger_entries()
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
self.update_asset_value()
|
self.update_asset_value()
|
||||||
self.update_inter_company_jv()
|
self.update_inter_company_jv()
|
||||||
@@ -218,8 +219,10 @@ class JournalEntry(AccountsController):
|
|||||||
"Repost Accounting Ledger Items",
|
"Repost Accounting Ledger Items",
|
||||||
"Unreconcile Payment",
|
"Unreconcile Payment",
|
||||||
"Unreconcile Payment Entries",
|
"Unreconcile Payment Entries",
|
||||||
|
"Advance Payment Ledger Entry",
|
||||||
)
|
)
|
||||||
self.make_gl_entries(1)
|
self.make_gl_entries(1)
|
||||||
|
self.make_advance_payment_ledger_entries()
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
self.unlink_advance_entry_reference()
|
self.unlink_advance_entry_reference()
|
||||||
self.unlink_asset_reference()
|
self.unlink_asset_reference()
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ class PaymentEntry(AccountsController):
|
|||||||
self.update_outstanding_amounts()
|
self.update_outstanding_amounts()
|
||||||
self.update_payment_schedule()
|
self.update_payment_schedule()
|
||||||
self.update_payment_requests()
|
self.update_payment_requests()
|
||||||
|
self.make_advance_payment_ledger_entries()
|
||||||
self.update_advance_paid() # advance_paid_status depends on the payment request amount
|
self.update_advance_paid() # advance_paid_status depends on the payment request amount
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
@@ -190,6 +191,7 @@ class PaymentEntry(AccountsController):
|
|||||||
"Repost Accounting Ledger Items",
|
"Repost Accounting Ledger Items",
|
||||||
"Unreconcile Payment",
|
"Unreconcile Payment",
|
||||||
"Unreconcile Payment Entries",
|
"Unreconcile Payment Entries",
|
||||||
|
"Advance Payment Ledger Entry",
|
||||||
)
|
)
|
||||||
super().on_cancel()
|
super().on_cancel()
|
||||||
self.make_gl_entries(cancel=1)
|
self.make_gl_entries(cancel=1)
|
||||||
@@ -197,6 +199,7 @@ class PaymentEntry(AccountsController):
|
|||||||
self.delink_advance_entry_references()
|
self.delink_advance_entry_references()
|
||||||
self.update_payment_schedule(cancel=1)
|
self.update_payment_schedule(cancel=1)
|
||||||
self.update_payment_requests(cancel=True)
|
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.update_advance_paid() # advance_paid_status depends on the payment request amount
|
||||||
self.set_status()
|
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
|
# 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(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
|
# check outstanding after advance allocation
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|||||||
@@ -362,10 +362,14 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
|
|||||||
# Assert 'Advance Paid'
|
# Assert 'Advance Paid'
|
||||||
so.reload()
|
so.reload()
|
||||||
pe.reload()
|
pe.reload()
|
||||||
self.assertEqual(so.advance_paid, 0)
|
self.assertEqual(so.advance_paid, 100)
|
||||||
self.assertEqual(len(pe.references), 0)
|
self.assertEqual(len(pe.references), 0)
|
||||||
self.assertEqual(pe.unallocated_amount, 100)
|
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):
|
def test_06_unreconcile_advance_from_payment_entry(self):
|
||||||
self.enable_advance_as_liability()
|
self.enable_advance_as_liability()
|
||||||
so1 = self.create_sales_order()
|
so1 = self.create_sales_order()
|
||||||
@@ -411,7 +415,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
|
|||||||
so2.reload()
|
so2.reload()
|
||||||
pe.reload()
|
pe.reload()
|
||||||
self.assertEqual(so1.advance_paid, 150)
|
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(len(pe.references), 1)
|
||||||
self.assertEqual(pe.unallocated_amount, 110)
|
self.assertEqual(pe.unallocated_amount, 110)
|
||||||
|
|
||||||
@@ -459,6 +463,6 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
|
|||||||
|
|
||||||
# Assert 'Advance Paid'
|
# Assert 'Advance Paid'
|
||||||
so.reload()
|
so.reload()
|
||||||
self.assertEqual(so.advance_paid, 0)
|
self.assertEqual(so.advance_paid, 1000)
|
||||||
|
|
||||||
self.disable_advance_as_liability()
|
self.disable_advance_as_liability()
|
||||||
|
|||||||
@@ -345,9 +345,21 @@ class AccountsController(TransactionBase):
|
|||||||
repost_doc.flags.ignore_links = True
|
repost_doc.flags.ignore_links = True
|
||||||
repost_doc.save(ignore_permissions=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):
|
def on_trash(self):
|
||||||
from erpnext.accounts.utils import delete_exchange_gain_loss_journal
|
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_repost_doctypes()
|
||||||
self._remove_references_in_unreconcile()
|
self._remove_references_in_unreconcile()
|
||||||
self.remove_serial_and_batch_bundle()
|
self.remove_serial_and_batch_bundle()
|
||||||
@@ -1935,21 +1947,23 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
return stock_items
|
return stock_items
|
||||||
|
|
||||||
def set_total_advance_paid(self):
|
def calculate_total_advance_from_ledger(self):
|
||||||
ple = frappe.qb.DocType("Payment Ledger Entry")
|
adv = frappe.qb.DocType("Advance Payment Ledger Entry")
|
||||||
party = self.customer if self.doctype == "Sales Order" else self.supplier
|
|
||||||
advance = (
|
advance = (
|
||||||
frappe.qb.from_(ple)
|
frappe.qb.from_(adv)
|
||||||
.select(ple.account_currency, Abs(Sum(ple.amount_in_account_currency)).as_("amount"))
|
.select(adv.currency.as_("account_currency"), Abs(Sum(adv.amount)).as_("amount"))
|
||||||
.where(
|
.where(
|
||||||
(ple.against_voucher_type == self.doctype)
|
(adv.against_voucher_type == self.doctype)
|
||||||
& (ple.against_voucher_no == self.name)
|
& (adv.against_voucher_no == self.name)
|
||||||
& (ple.party == party)
|
& (adv.company == self.company)
|
||||||
& (ple.delinked == 0)
|
|
||||||
& (ple.company == self.company)
|
|
||||||
)
|
)
|
||||||
.run(as_dict=True)
|
.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:
|
if advance:
|
||||||
advance = advance[0]
|
advance = advance[0]
|
||||||
@@ -2546,6 +2560,65 @@ class AccountsController(TransactionBase):
|
|||||||
repost_ledger.insert()
|
repost_ledger.insert()
|
||||||
repost_ledger.submit()
|
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()
|
@frappe.whitelist()
|
||||||
def get_tax_rate(account_head):
|
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.v14_0.update_flag_for_return_invoices #2024-03-22
|
||||||
erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request
|
erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request
|
||||||
erpnext.patches.v14_0.update_pos_return_ledger_entries #2024-08-16
|
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
|
# below migration patch should always run last
|
||||||
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
|
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
|
||||||
erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20
|
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