From 74619269f0fa5d380c820bc12ab59cb7a06669c8 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 2 Jun 2023 17:13:51 +0530 Subject: [PATCH 01/29] feat: Record Advance Payments as Liability Ability to let user record advance payments as liability instead of a negative asset. Issue #34282 --- .../doctype/party_account/party_account.json | 20 +- .../doctype/payment_entry/payment_entry.js | 53 +++- .../doctype/payment_entry/payment_entry.py | 5 +- .../payment_reconciliation.js | 53 +++- .../payment_reconciliation.json | 23 +- .../payment_reconciliation.py | 27 +- .../purchase_invoice/purchase_invoice.py | 7 + .../purchase_invoice_advance.json | 15 +- .../doctype/sales_invoice/sales_invoice.py | 6 + .../sales_invoice_advance.json | 15 +- erpnext/accounts/party.py | 32 ++- erpnext/accounts/utils.py | 77 ++--- erpnext/buying/doctype/supplier/supplier.js | 23 ++ erpnext/buying/doctype/supplier/supplier.json | 9 +- erpnext/controllers/accounts_controller.py | 264 ++++++++++++------ erpnext/selling/doctype/customer/customer.js | 22 ++ .../selling/doctype/customer/customer.json | 7 +- erpnext/setup/doctype/company/company.js | 4 +- erpnext/setup/doctype/company/company.json | 38 ++- .../doctype/customer_group/customer_group.js | 26 +- .../customer_group/customer_group.json | 5 +- 21 files changed, 559 insertions(+), 172 deletions(-) diff --git a/erpnext/accounts/doctype/party_account/party_account.json b/erpnext/accounts/doctype/party_account/party_account.json index 69330577ab3..719b474f1f2 100644 --- a/erpnext/accounts/doctype/party_account/party_account.json +++ b/erpnext/accounts/doctype/party_account/party_account.json @@ -6,7 +6,9 @@ "engine": "InnoDB", "field_order": [ "company", - "account" + "account", + "advances_received_account", + "advances_paid_account" ], "fields": [ { @@ -22,14 +24,26 @@ "fieldname": "account", "fieldtype": "Link", "in_list_view": 1, - "label": "Account", + "label": "Default Account", + "options": "Account" + }, + { + "fieldname": "advances_received_account", + "fieldtype": "Link", + "label": "Advances Received Account", + "options": "Account" + }, + { + "fieldname": "advances_paid_account", + "fieldtype": "Link", + "label": "Advances Paid Account", "options": "Account" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-04-04 12:31:02.994197", + "modified": "2023-06-02 13:00:06.885744", "modified_by": "Administrator", "module": "Accounts", "name": "Party Account", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 2843824934c..7bd3bb8710d 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -18,14 +18,20 @@ frappe.ui.form.on('Payment Entry', { }, setup: function(frm) { + advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": frm.doc.company}, "book_advance_payments_as_liability"); + + if(advance_payments_as_liability && frm.doc.payment_type == 'Receive'){ + account_type = "Payable"; + } + else{ + account_type = "Receivable"; + } + frm.set_query("paid_from", function() { frm.events.validate_company(frm); - - var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ? - ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; return { filters: { - "account_type": ["in", account_types], + "account_type": account_type, "is_group": 0, "company": frm.doc.company } @@ -74,12 +80,15 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("paid_to", function() { frm.events.validate_company(frm); - - var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ? - ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; + if(advance_payments_as_liability && in_list(['Receive', 'Internal Transfer'], cur_frm.doc.payment_type)){ + account_type = ["Bank", "Cash"]; + } + else{ + account_type = "Receivable"; + } return { filters: { - "account_type": ["in", account_types], + "account_type": ["in", account_type], "is_group": 0, "company": frm.doc.company } @@ -270,6 +279,25 @@ frappe.ui.form.on('Payment Entry', { }, payment_type: function(frm) { + advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": frm.doc.company}, "book_advance_payments_as_liability"); + + if(advance_payments_as_liability && frm.doc.payment_type == 'Receive'){ + account_type = ["Payable"]; + } + else{ + account_type = ["Bank", "Cash"]; + } + + frm.set_query("paid_from", function() { + frm.events.validate_company(frm); + return { + filters: { + "account_type": ["in", account_type], + "is_group": 0, + "company": frm.doc.company + } + } + }); if(frm.doc.payment_type == "Internal Transfer") { $.each(["party", "party_balance", "paid_from", "paid_to", "references", "total_allocated_amount"], function(i, field) { @@ -318,6 +346,10 @@ frappe.ui.form.on('Payment Entry', { } }, + company: function(frm){ + frm.trigger('party'); + }, + party: function(frm) { if (frm.doc.contact_email || frm.doc.contact_person) { frm.set_value("contact_email", ""); @@ -332,7 +364,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_party_account_based_on_party = true; let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; - + return frappe.call({ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_details", args: { @@ -340,7 +372,8 @@ frappe.ui.form.on('Payment Entry', { party_type: frm.doc.party_type, party: frm.doc.party, date: frm.doc.posting_date, - cost_center: frm.doc.cost_center + cost_center: frm.doc.cost_center, + is_advance: !(frm.doc.references) }, callback: function(r, rt) { if(r.message) { diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 3df48e22ad6..ed2158a6491 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1563,13 +1563,12 @@ def get_negative_outstanding_invoices( @frappe.whitelist() -def get_party_details(company, party_type, party, date, cost_center=None): +def get_party_details(company, party_type, party, date, cost_center=None, is_advance=False): bank_account = "" if not frappe.db.exists(party_type, party): frappe.throw(_("Invalid {0}: {1}").format(party_type, party)) - party_account = get_party_account(party_type, party, company) - + party_account = get_party_account(party_type, party, company, is_advance) account_currency = get_account_currency(party_account) account_balance = get_balance_on(party_account, date, cost_center=cost_center) _party_name = "title" if party_type == "Shareholder" else party_type.lower() + "_name" diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 08d38dde474..2e9628c9f0a 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -29,6 +29,26 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo }; }); + this.frm.set_query('default_advances_received_account', () => { + return { + filters: { + "company": this.frm.doc.company, + "is_group": 0, + "root_type": "Liability" + } + }; + }); + + this.frm.set_query('default_advances_paid_account', () => { + return { + filters: { + "company": this.frm.doc.company, + "is_group": 0, + "root_type": "Asset" + } + }; + }); + this.frm.set_query('bank_cash_account', () => { return { filters:[ @@ -124,7 +144,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo this.frm.trigger("clear_child_tables"); if (!this.frm.doc.receivable_payable_account && this.frm.doc.party_type && this.frm.doc.party) { - return frappe.call({ + frappe.call({ method: "erpnext.accounts.party.get_party_account", args: { company: this.frm.doc.company, @@ -136,7 +156,38 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo this.frm.set_value("receivable_payable_account", r.message); } this.frm.refresh(); + } + }); + frappe.call({ + method: "erpnext.accounts.party.get_party_account", + args: { + company: this.frm.doc.company, + party_type: this.frm.doc.party_type, + party: this.frm.doc.party, + is_advance: 1 + }, + callback: (r) => { + if (!r.exc && r.message) { + this.frm.set_value("default_advances_received_account", r.message); + } + this.frm.refresh(); + } + }); + + frappe.call({ + method: "erpnext.accounts.party.get_party_account", + args: { + company: this.frm.doc.company, + party_type: (this.frm.doc.party_type == 'Customer')?'Supplier':'Customer', + party: this.frm.doc.party, + is_advance: 1 + }, + callback: (r) => { + if (!r.exc && r.message) { + this.frm.set_value("default_advances_paid_account", r.message); + } + this.frm.refresh(); } }); } diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index 18d34850850..21b8392d609 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -7,9 +7,11 @@ "field_order": [ "company", "party_type", - "column_break_4", "party", + "column_break_4", "receivable_payable_account", + "default_advances_received_account", + "default_advances_paid_account", "col_break1", "from_invoice_date", "from_payment_date", @@ -185,13 +187,30 @@ "fieldtype": "Link", "label": "Cost Center", "options": "Cost Center" + }, + { + "depends_on": "eval:doc.party_type", + "fieldname": "default_advances_received_account", + "fieldtype": "Link", + "label": "Default Advances Received Account", + "mandatory_depends_on": "doc.party_type", + "options": "Account", + "reqd": 1 + }, + { + "depends_on": "eval:doc.party_type", + "fieldname": "default_advances_paid_account", + "fieldtype": "Link", + "label": "Default Advances Paid Account", + "mandatory_depends_on": "doc.party_type", + "options": "Account" } ], "hide_toolbar": 1, "icon": "icon-resize-horizontal", "issingle": 1, "links": [], - "modified": "2022-04-29 15:37:10.246831", + "modified": "2023-06-02 14:32:27.276083", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index cc2b9420cc2..9b03d36a8e9 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -19,7 +19,8 @@ from erpnext.accounts.utils import ( reconcile_against_document, ) from erpnext.controllers.accounts_controller import get_advance_payment_entries - +from erpnext.controllers.accounts_controller import make_advance_liability_entry +from erpnext.accounts.general_ledger import make_gl_entries class PaymentReconciliation(Document): def __init__(self, *args, **kwargs): @@ -56,12 +57,26 @@ class PaymentReconciliation(Document): self.add_payment_entries(non_reconciled_payments) def get_payment_entries(self): + receivable_payable_account = self.receivable_payable_account + default_advances_account = self.default_advances_received_account + party_account = [receivable_payable_account, default_advances_account] order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" - condition = self.get_conditions(get_payments=True) + condition = frappe._dict( + { + "company": self.get("company"), + "get_payments": True, + "cost_center": self.get("cost_center"), + "from_payment_date": self.get("from_payment_date"), + "to_payment_date": self.get("to_payment_date"), + "maximum_payment_amount": self.get("maximum_payment_amount"), + "minimum_payment_amount": self.get("minimum_payment_amount") + } + ) + payment_entries = get_advance_payment_entries( - self.party_type, + self.party_type, self.party, - self.receivable_payable_account, + party_account, order_doctype, against_all_orders=True, limit=self.payment_limit, @@ -319,6 +334,10 @@ class PaymentReconciliation(Document): for row in self.get("allocation"): reconciled_entry = [] if row.invoice_number and row.allocated_amount: + if row.invoice_type in ["Sales Invoice", "Purchase Invoice"]: + gl_entries = [] + make_advance_liability_entry(gl_entries, row.reference_name, row.allocated_amount, row.invoice_number, self.party_type) + make_gl_entries(gl_entries) if row.reference_type in ["Sales Invoice", "Purchase Invoice"]: reconciled_entry = dr_or_cr_notes else: diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 868a150edf1..dfea8e9049d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -34,6 +34,7 @@ from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accoun from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.buying.utils import check_on_hold_or_closed_status from erpnext.controllers.accounts_controller import validate_account_head +from erpnext.controllers.accounts_controller import make_advance_liability_entry from erpnext.controllers.buying_controller import BuyingController from erpnext.stock import get_warehouse_account_map from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( @@ -580,6 +581,12 @@ class PurchaseInvoice(BuyingController): gl_entries = [] self.make_supplier_gl_entry(gl_entries) + + advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": self.company}, "book_advance_payments_as_liability") + if advance_payments_as_liability: + for advance_entry in self.advances: + make_advance_liability_entry(gl_entries, advance_entry.reference_name, advance_entry.allocated_amount, invoice=self.name, party_type="Supplier") + self.make_item_gl_entries(gl_entries) self.make_precision_loss_gl_entry(gl_entries) diff --git a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json index 9fcbf5c6339..9082115f23d 100644 --- a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json +++ b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json @@ -14,7 +14,8 @@ "advance_amount", "allocated_amount", "exchange_gain_loss", - "ref_exchange_rate" + "ref_exchange_rate", + "account" ], "fields": [ { @@ -111,13 +112,20 @@ "label": "Reference Exchange Rate", "non_negative": 1, "read_only": 1 + }, + { + "fieldname": "account", + "fieldtype": "Link", + "label": "Account", + "options": "Account", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-26 15:47:28.167371", + "modified": "2023-06-01 16:56:48.530169", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Advance", @@ -125,5 +133,6 @@ "permissions": [], "quick_entry": 1, "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 7454332cd3d..419628eaffc 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -33,6 +33,7 @@ from erpnext.assets.doctype.asset.depreciation import ( reverse_depreciation_entry_made_after_disposal, ) from erpnext.controllers.accounts_controller import validate_account_head +from erpnext.controllers.accounts_controller import make_advance_liability_entry from erpnext.controllers.selling_controller import SellingController from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data from erpnext.setup.doctype.company.company import update_company_current_month_sales @@ -1064,6 +1065,11 @@ class SalesInvoice(SellingController): gl_entries = [] self.make_customer_gl_entry(gl_entries) + + advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": self.company}, "book_advance_payments_as_liability") + if advance_payments_as_liability: + for advance_entry in self.advances: + make_advance_liability_entry(gl_entries, advance_entry.reference_name, advance_entry.allocated_amount, invoice=self.name, party_type="Customer") self.make_tax_gl_entries(gl_entries) self.make_exchange_gain_loss_gl_entries(gl_entries) diff --git a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json index f92b57a45e1..aa52b1cac2d 100644 --- a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json +++ b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json @@ -14,7 +14,8 @@ "advance_amount", "allocated_amount", "exchange_gain_loss", - "ref_exchange_rate" + "ref_exchange_rate", + "account" ], "fields": [ { @@ -112,13 +113,20 @@ "label": "Reference Exchange Rate", "non_negative": 1, "read_only": 1 + }, + { + "fieldname": "account", + "fieldtype": "Link", + "label": "Account", + "options": "Account", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-26 15:47:46.911595", + "modified": "2023-05-31 11:47:00.191681", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Advance", @@ -126,5 +134,6 @@ "permissions": [], "quick_entry": 1, "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index f86dd8f57ee..3be42758885 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -365,7 +365,7 @@ def set_account_and_due_date( @frappe.whitelist() -def get_party_account(party_type, party=None, company=None): +def get_party_account(party_type, party=None, company=None, is_advance=False): """Returns the account for the given `party`. Will first search in party (Customer / Supplier) record, if not found, will search in group (Customer Group / Supplier Group), @@ -380,6 +380,11 @@ def get_party_account(party_type, party=None, company=None): return frappe.get_cached_value("Company", company, default_account_name) + advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": company}, "book_advance_payments_as_liability") + + if is_advance and advance_payments_as_liability and party_type in ["Customer", "Supplier"]: + return get_party_advance_account(party_type, party, company) + account = frappe.db.get_value( "Party Account", {"parenttype": party_type, "parent": party, "company": company}, "account" ) @@ -409,6 +414,26 @@ def get_party_account(party_type, party=None, company=None): return account +def get_party_advance_account(party_type, party, company): + account_name = 'advances_received_account' if party_type == 'Customer' else 'advances_paid_account' + account = frappe.db.get_value( + "Party Account", {"parenttype": party_type, "parent": party, "company": company}, account_name + ) + + if not account: + party_group_doctype = "Customer Group" if party_type == "Customer" else "Supplier Group" + group = frappe.get_cached_value(party_type, party, scrub(party_group_doctype)) + account = frappe.db.get_value( + "Party Account", + {"parenttype": party_group_doctype, "parent": group, "company": company}, + account_name, + ) + + if not account: + account = frappe.get_cached_value("Company", company, "default_" + account_name) + + return account + @frappe.whitelist() def get_party_bank_account(party_type, party): return frappe.db.get_value( @@ -515,7 +540,10 @@ def validate_party_accounts(doc): ) # validate if account is mapped for same company - validate_account_head(account.idx, account.account, account.company) + if account.account: + validate_account_head(account.idx, account.account, account.company) + if account.advance_account: + validate_account_head(account.idx, account.advance_account, account.company) @frappe.whitelist() diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 0ee06e8239c..5abc64315cc 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -495,50 +495,51 @@ def check_if_advance_entry_modified(args): ret = None if args.voucher_type == "Journal Entry": - ret = frappe.db.sql( - """ - select t2.{dr_or_cr} from `tabJournal Entry` t1, `tabJournal Entry Account` t2 - where t1.name = t2.parent and t2.account = %(account)s - and t2.party_type = %(party_type)s and t2.party = %(party)s - and (t2.reference_type is null or t2.reference_type in ('', 'Sales Order', 'Purchase Order')) - and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s - and t1.docstatus=1 """.format( - dr_or_cr=args.get("dr_or_cr") - ), - args, + journal_entry = frappe.qb.DocType("Journal Entry") + journal_acc = frappe.qb.DocType("Journal Entry Account") + + q = (frappe.qb.from_(journal_entry) + .innerjoin(journal_acc) + .on(journal_entry.name == journal_acc.parent) ) + + if args.get("dr_or_cr") == 'debit_in_account_currency': + q = q.select(journal_acc.debit_in_account_currency) + else: + q = q.select(journal_acc.credit_in_account_currency) + + q = q.where((journal_acc.account == args.get("account")) + &((journal_acc.party_type == args.get("party_type"))) + &((journal_acc.party == args.get("party"))) + &((journal_acc.reference_type == None) | (journal_acc.reference_type.isin(['', 'Sales Order', 'Purchase Order']))) + &((journal_entry.name == args.get("voucher_no"))) + &((journal_acc.name == args.get("voucher_detail_no"))) + &((journal_entry.docstatus == 1)) + ) + else: - party_account_field = ( - "paid_from" if erpnext.get_party_account_type(args.party_type) == "Receivable" else "paid_to" + payment_entry = frappe.qb.DocType("Payment Entry") + payment_ref = frappe.qb.DocType("Payment Entry Reference") + + q = (frappe.qb.from_(payment_entry) + .select(payment_entry.name) + .where(payment_entry.name == args.get("voucher_no")) + .where(payment_entry.docstatus == 1) + .where(payment_entry.party_type == args.get("party_type")) + .where(payment_entry.party == args.get("party")) ) if args.voucher_detail_no: - ret = frappe.db.sql( - """select t1.name - from `tabPayment Entry` t1, `tabPayment Entry Reference` t2 - where - t1.name = t2.parent and t1.docstatus = 1 - and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s - and t1.party_type = %(party_type)s and t1.party = %(party)s and t1.{0} = %(account)s - and t2.reference_doctype in ('', 'Sales Order', 'Purchase Order') - and t2.allocated_amount = %(unreconciled_amount)s - """.format( - party_account_field - ), - args, - ) + q = ( q.inner_join(payment_ref) + .on(payment_entry.name == payment_ref.parent) + .where(payment_ref.name == args.get("voucher_detail_no")) + .where(payment_ref.reference_doctype.isin(('', 'Sales Order', 'Purchase Order'))) + .where(payment_ref.allocated_amount == args.get("unreconciled_amount")) + ) else: - ret = frappe.db.sql( - """select name from `tabPayment Entry` - where - name = %(voucher_no)s and docstatus = 1 - and party_type = %(party_type)s and party = %(party)s and {0} = %(account)s - and unallocated_amount = %(unreconciled_amount)s - """.format( - party_account_field - ), - args, - ) + q = q.where(payment_entry.unallocated_amount == args.get("unreconciled_amount")) + + ret = q.run(as_dict=True) if not ret: throw(_("""Payment Entry has been modified after you pulled it. Please pull it again.""")) diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 1ae6f036474..9e217b5113c 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -17,6 +17,29 @@ frappe.ui.form.on("Supplier", { } } }); + + frm.set_query('advances_received_account', 'accounts', function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Asset', + "company": d.company, + "is_group": 0 + } + } + }); + + frm.set_query('advances_paid_account', 'accounts', function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Liability', + "company": d.company, + "is_group": 0 + } + } + }); + frm.set_query("default_bank_account", function() { return { filters: { diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 1bf7f589e23..86e5eeecbf6 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -53,6 +53,7 @@ "primary_address", "accounting_tab", "payment_terms", + "default_accounts_section", "accounts", "settings_tab", "allow_purchase_invoice_creation_without_purchase_order", @@ -445,6 +446,11 @@ { "fieldname": "column_break_59", "fieldtype": "Column Break" + }, + { + "fieldname": "default_accounts_section", + "fieldtype": "Section Break", + "label": "Default Accounts" } ], "icon": "fa fa-user", @@ -457,7 +463,7 @@ "link_fieldname": "party" } ], - "modified": "2023-02-18 11:05:50.592270", + "modified": "2023-05-29 15:23:11.709415", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", @@ -489,7 +495,6 @@ "read": 1, "report": 1, "role": "Purchase Master Manager", - "set_user_permissions": 1, "share": 1, "write": 1 }, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 20b332e7827..ac32fd352ce 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -8,6 +8,8 @@ import frappe from frappe import _, bold, throw from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied from frappe.query_builder.functions import Abs, Sum +from frappe.query_builder.custom import ConstantColumn + from frappe.utils import ( add_days, add_months, @@ -871,24 +873,30 @@ class AccountsController(TransactionBase): "allocated_amount": allocated_amount, "ref_exchange_rate": flt(d.exchange_rate), # exchange_rate of advance entry } + if d.get("paid_from"): + advance_row["account"] = d.paid_from + if d.get("paid_to"): + advance_row["account"] = d.paid_to self.append("advances", advance_row) def get_advance_entries(self, include_unallocated=True): if self.doctype == "Sales Invoice": - party_account = self.debit_to party_type = "Customer" party = self.customer amount_field = "credit_in_account_currency" order_field = "sales_order" order_doctype = "Sales Order" + party_account = frappe.db.get_values("Company", {"company_name": self.company}, ["default_receivable_account", "default_advances_received_account"]) else: - party_account = self.credit_to party_type = "Supplier" party = self.supplier amount_field = "debit_in_account_currency" order_field = "purchase_order" order_doctype = "Purchase Order" + party_account = frappe.db.get_values("Company", {"company_name": self.company}, ["default_receivable_account", "default_advances_paid_account"]) + + party_account = list(party_account[0]) order_list = list(set(d.get(order_field) for d in self.get("items") if d.get(order_field))) @@ -2136,45 +2144,41 @@ def get_advance_journal_entries( order_list, include_unallocated=True, ): - dr_or_cr = ( - "credit_in_account_currency" if party_type == "Customer" else "debit_in_account_currency" - ) - - conditions = [] - if include_unallocated: - conditions.append("ifnull(t2.reference_name, '')=''") + journal_entry = frappe.qb.DocType('Journal Entry') + journal_acc = frappe.qb.DocType('Journal Entry Account') + q = (frappe.qb.from_(journal_entry) + .inner_join(journal_acc) + .on(journal_entry.name == journal_acc.parent) + .select( + ConstantColumn('Journal Entry').as_('reference_type'), + (journal_entry.name).as_('reference_name'), + (journal_entry.remark).as_('remarks'), + (journal_acc.debit_in_account_currency if party_type == 'Supplier' else journal_acc.credit_in_account_currency).as_('amount'), + (journal_acc.name).as_('reference_row'), + (journal_acc.reference_name).as_('against_order'), + (journal_acc.exchange_rate) + ) + .where(journal_acc.account.isin(party_account) + & (journal_acc.party_type == party_type) + & (journal_acc.party == party) + & (journal_acc.is_advance == 'Yes') + & (journal_entry.docstatus == 1) + ) + ) + if party_type == "Customer": + q = q.where(journal_acc.credit_in_account_currency > 0) if order_list: - order_condition = ", ".join(["%s"] * len(order_list)) - conditions.append( - " (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))".format( - order_doctype, order_condition - ) - ) + q = q.where(journal_acc.reference_type == order_doctype) + if include_unallocated: + q = q.where(journal_acc.reference_name.isin(order_list) + |(journal_acc.reference_name == '')) + else: + q = q.where(journal_acc.reference_name.isin(order_list)) - reference_condition = " and (" + " or ".join(conditions) + ")" if conditions else "" - - # nosemgrep - journal_entries = frappe.db.sql( - """ - select - 'Journal Entry' as reference_type, t1.name as reference_name, - t1.remark as remarks, t2.{0} as amount, t2.name as reference_row, - t2.reference_name as against_order, t2.exchange_rate - from - `tabJournal Entry` t1, `tabJournal Entry Account` t2 - where - t1.name = t2.parent and t2.account = %s - and t2.party_type = %s and t2.party = %s - and t2.is_advance = 'Yes' and t1.docstatus = 1 - and {1} > 0 {2} - order by t1.posting_date""".format( - amount_field, dr_or_cr, reference_condition - ), - [party_account, party_type, party] + order_list, - as_dict=1, - ) + q = q.orderby(journal_entry.posting_date) + journal_entries = q.run(as_dict=True) return list(journal_entries) @@ -2189,65 +2193,76 @@ def get_advance_payment_entries( limit=None, condition=None, ): - party_account_field = "paid_from" if party_type == "Customer" else "paid_to" - currency_field = ( - "paid_from_account_currency" if party_type == "Customer" else "paid_to_account_currency" - ) + + q = build_query(party_type, party, party_account, order_doctype, order_list, include_unallocated, against_all_orders, limit, condition) + + payment_entries = q.run(as_dict=True) + + return list(payment_entries) + +def build_query(party_type, party, party_account, order_doctype, order_list, include_unallocated, against_all_orders, limit, condition): payment_type = "Receive" if party_type == "Customer" else "Pay" - exchange_rate_field = ( - "source_exchange_rate" if payment_type == "Receive" else "target_exchange_rate" - ) + payment_entry = frappe.qb.DocType('Payment Entry') + payment_ref = frappe.qb.DocType('Payment Entry Reference') - payment_entries_against_order, unallocated_payment_entries = [], [] - limit_cond = "limit %s" % limit if limit else "" - - if order_list or against_all_orders: - if order_list: - reference_condition = " and t2.reference_name in ({0})".format( - ", ".join(["%s"] * len(order_list)) - ) - else: - reference_condition = "" - order_list = [] - - payment_entries_against_order = frappe.db.sql( - """ - select - 'Payment Entry' as reference_type, t1.name as reference_name, - t1.remarks, t2.allocated_amount as amount, t2.name as reference_row, - t2.reference_name as against_order, t1.posting_date, - t1.{0} as currency, t1.{4} as exchange_rate - from `tabPayment Entry` t1, `tabPayment Entry Reference` t2 - where - t1.name = t2.parent and t1.{1} = %s and t1.payment_type = %s - and t1.party_type = %s and t1.party = %s and t1.docstatus = 1 - and t2.reference_doctype = %s {2} - order by t1.posting_date {3} - """.format( - currency_field, party_account_field, reference_condition, limit_cond, exchange_rate_field - ), - [party_account, payment_type, party_type, party, order_doctype] + order_list, - as_dict=1, + q = (frappe.qb.from_(payment_entry) + .select( + ConstantColumn('Payment Entry').as_('reference_type'), + (payment_entry.name).as_('reference_name'), + payment_entry.posting_date, + (payment_entry.remarks).as_('remarks') ) + .where(payment_entry.payment_type == payment_type) + .where(payment_entry.party_type == party_type) + .where(payment_entry.party == party) + .where(payment_entry.docstatus == 1) + ) + + if party_type == "Customer": + q = q.select(payment_entry.paid_from_account_currency) + q = q.select(payment_entry.paid_from) + q = q.where(payment_entry.paid_from.isin(party_account)) + else: + q = q.select(payment_entry.paid_to_account_currency) + q = q.select(payment_entry.paid_to) + q = q.where(payment_entry.paid_to.isin(party_account)) + + if payment_type == "Receive": + q = q.select(payment_entry.source_exchange_rate) + else: + q.select(payment_entry.target_exchange_rate) if include_unallocated: - unallocated_payment_entries = frappe.db.sql( - """ - select 'Payment Entry' as reference_type, name as reference_name, posting_date, - remarks, unallocated_amount as amount, {2} as exchange_rate, {3} as currency - from `tabPayment Entry` - where - {0} = %s and party_type = %s and party = %s and payment_type = %s - and docstatus = 1 and unallocated_amount > 0 {condition} - order by posting_date {1} - """.format( - party_account_field, limit_cond, exchange_rate_field, currency_field, condition=condition or "" - ), - (party_account, party_type, party, payment_type), - as_dict=1, - ) + q = q.select((payment_entry.unallocated_amount).as_('amount')) + q = q.where(payment_entry.unallocated_amount>0) - return list(payment_entries_against_order) + list(unallocated_payment_entries) + if condition: + q = q.where(payment_entry.company == condition["company"]) + q = q.where(payment_entry.posting_date >= condition["from_payment_date"]) if condition.get("from_payment_date") else q + q = q.where(payment_entry.posting_date <= condition["to_payment_date"]) if condition.get("to_payment_date") else q + if condition.get("get_payments") == True: + q = q.where(payment_entry.cost_center == condition["cost_center"]) if condition.get("cost_center") else q + q = q.where(payment_entry.unallocated_amount >= condition["minimum_payment_amount"]) if condition.get("minimum_payment_amount") else q + q = q.where(payment_entry.unallocated_amount <= condition["maximum_payment_amount"]) if condition.get("maximum_payment_amount") else q + else: + q = q.where(payment_entry.total_debit >= condition["minimum_payment_amount"]) if condition.get("minimum_payment_amount") else q + q = q.where(payment_entry.total_debit <= condition["maximum_payment_amount"]) if condition.get("maximum_payment_amount") else q + + elif order_list or against_all_orders: + q = q.inner_join(payment_ref).on(payment_entry.name == payment_ref.parent) + q = q.select( + (payment_ref.allocated_amount).as_('amount'), + (payment_ref.name).as_('reference_row'), + (payment_ref.reference_name).as_('against_order'), + payment_ref.reference_doctype == order_doctype + ) + + if order_list: + q = q.where(payment_ref.reference_name.isin(order_list)) + + q = q.orderby(payment_entry.posting_date) + q = q.limit(limit) if limit else q + return q def update_invoice_status(): @@ -2846,3 +2861,72 @@ def validate_regional(doc): @erpnext.allow_regional def validate_einvoice_fields(doc): pass + +def make_advance_liability_entry(gl_entries, pe, allocated_amount, invoice, party_type): + pe = frappe.get_doc("Payment Entry", pe) + if party_type=="Customer": + invoice = frappe.get_doc("Sales Invoice", invoice) + account = pe.paid_from + dr_or_cr = "debit" + rev = "credit" + against = invoice.debit_to + party = invoice.customer + voucher_type = "Sales Invoice" + else: + invoice = frappe.get_doc("Purchase Invoice", invoice) + account = pe.paid_to + dr_or_cr = "credit" + rev = "debit" + against = invoice.credit_to + party = invoice.supplier + voucher_type = "Purchase Invoice" + gl_entries.append(invoice.get_gl_dict( + { + "account": account, + "party_type": party_type, + "party": party, + "due_date": invoice.due_date, + "against": against, + dr_or_cr: allocated_amount, + dr_or_cr + "_in_account_currency": allocated_amount, + rev: 0, + rev + "_in_account_currency": 0, + "against_voucher": invoice.return_against + if cint(invoice.is_return) and invoice.return_against + else invoice.name, + "against_voucher_type": invoice.doctype, + "cost_center": invoice.cost_center, + "project": invoice.project, + "voucher_type": voucher_type, + "voucher_no": invoice.name + }, + invoice.party_account_currency, + item=invoice, + )) + + (dr_or_cr, rev) = ("credit", "debit") if party_type=="Customer" else ("debit", "credit") + gl_entries.append(invoice.get_gl_dict( + { + "account": against, + "party_type": party_type, + "party": party, + "due_date": invoice.due_date, + "against": account, + dr_or_cr: allocated_amount, + dr_or_cr + "_in_account_currency": allocated_amount, + rev: 0, + rev + "_in_account_currency": 0, + "against_voucher": invoice.return_against + if cint(invoice.is_return) and invoice.return_against + else invoice.name, + "against_voucher_type": invoice.doctype, + "cost_center": invoice.cost_center, + "project": invoice.project, + "voucher_type": voucher_type, + "voucher_no": invoice.name + }, + invoice.party_account_currency, + item=invoice, + )) + + diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index b53f339229b..6dac6927471 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -34,6 +34,28 @@ frappe.ui.form.on("Customer", { filters: filters } }); + + frm.set_query('advances_received_account', 'accounts', function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Liability', + "company": d.company, + "is_group": 0 + } + } + }); + + frm.set_query('advances_paid_account', 'accounts', function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Asset', + "company": d.company, + "is_group": 0 + } + } + }); if (frm.doc.__islocal == 1) { frm.set_value("represents_company", ""); diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index c133cd3152d..0050279a4bd 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -334,13 +334,13 @@ { "fieldname": "default_receivable_accounts", "fieldtype": "Section Break", - "label": "Default Receivable Accounts" + "label": "Default Accounts" }, { "description": "Mention if a non-standard receivable account", "fieldname": "accounts", "fieldtype": "Table", - "label": "Receivable Accounts", + "label": "Accounts", "options": "Party Account" }, { @@ -568,7 +568,7 @@ "link_fieldname": "party" } ], - "modified": "2023-02-18 11:04:46.343527", + "modified": "2023-05-29 14:29:17.789578", "modified_by": "Administrator", "module": "Selling", "name": "Customer", @@ -607,7 +607,6 @@ "read": 1, "report": 1, "role": "Sales Master Manager", - "set_user_permissions": 1, "share": 1, "write": 1 }, diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index e50ce449e45..81919af4e1d 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -226,7 +226,9 @@ erpnext.company.setup_queries = function(frm) { ["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}], ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}], ["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}], - ["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}] + ["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}], + ["default_advances_received_account", {"root_type": "Liability"}], + ["default_advances_paid_account", {"root_type": "Asset"}], ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index f087d996ffe..369e139eef4 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -70,6 +70,11 @@ "payment_terms", "cost_center", "default_finance_book", + "advance_payments_section", + "book_advance_payments_as_liability", + "default_advances_received_account", + "column_break_cui0", + "default_advances_paid_account", "auto_accounting_for_stock_settings", "enable_perpetual_inventory", "enable_provisional_accounting_for_non_stock_items", @@ -694,6 +699,37 @@ "label": "Default Provisional Account", "no_copy": 1, "options": "Account" + }, + { + "default": "0", + "fieldname": "book_advance_payments_as_liability", + "fieldtype": "Check", + "label": "Book Advance Payments as Liability" + }, + { + "fieldname": "advance_payments_section", + "fieldtype": "Section Break", + "label": "Advance Payments" + }, + { + "depends_on": "eval:doc.book_advance_payments_as_liability", + "fieldname": "default_advances_received_account", + "fieldtype": "Link", + "label": "Default Advances Received Account", + "mandatory_depends_on": "book_advance_payments_as_liability", + "options": "Account" + }, + { + "depends_on": "eval:doc.book_advance_payments_as_liability", + "fieldname": "default_advances_paid_account", + "fieldtype": "Link", + "label": "Default Advances Paid Account", + "mandatory_depends_on": "book_advance_payments_as_liability", + "options": "Account" + }, + { + "fieldname": "column_break_cui0", + "fieldtype": "Column Break" } ], "icon": "fa fa-building", @@ -701,7 +737,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2022-08-16 16:09:02.327724", + "modified": "2023-06-02 13:11:41.939016", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js index 44a50191209..ef556c774df 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.js +++ b/erpnext/setup/doctype/customer_group/customer_group.js @@ -30,9 +30,31 @@ cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(d var d = locals[cdt][cdn]; return { filters: { - 'account_type': 'Receivable', - 'company': d.company, + "account_type": 'Receivable', + "company": d.company, "is_group": 0 } } } + +cur_frm.fields_dict['accounts'].grid.get_field('advances_received_account').get_query = function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Liability', + "company": d.company, + "is_group": 0 + } + } +} + +cur_frm.fields_dict['accounts'].grid.get_field('advances_paid_account').get_query = function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Asset', + "company": d.company, + "is_group": 0 + } + } +} \ No newline at end of file diff --git a/erpnext/setup/doctype/customer_group/customer_group.json b/erpnext/setup/doctype/customer_group/customer_group.json index d6a431ea616..4c36bc77abe 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.json +++ b/erpnext/setup/doctype/customer_group/customer_group.json @@ -113,7 +113,7 @@ { "fieldname": "default_receivable_account", "fieldtype": "Section Break", - "label": "Default Receivable Account" + "label": "Default Accounts" }, { "depends_on": "eval:!doc.__islocal", @@ -139,7 +139,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2022-12-24 11:15:17.142746", + "modified": "2023-06-02 13:40:34.435822", "modified_by": "Administrator", "module": "Setup", "name": "Customer Group", @@ -171,7 +171,6 @@ "read": 1, "report": 1, "role": "Sales Master Manager", - "set_user_permissions": 1, "share": 1, "write": 1 }, From 4ee163742a705083bd9b00e36aac9099efadaabd Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 8 Jun 2023 13:15:23 +0530 Subject: [PATCH 02/29] fix: Using one field for both advance liability accounts --- .../doctype/party_account/party_account.json | 15 +- .../doctype/payment_entry/payment_entry.js | 54 +--- .../doctype/payment_entry/payment_entry.json | 2 +- .../doctype/payment_entry/payment_entry.py | 116 ++++++-- .../payment_entry_reference.json | 11 +- .../payment_reconciliation.js | 32 +- .../payment_reconciliation.json | 17 +- .../payment_reconciliation.py | 31 +- .../purchase_invoice/purchase_invoice.json | 5 +- .../sales_invoice/test_sales_invoice.py | 4 +- erpnext/accounts/party.py | 18 +- erpnext/accounts/utils.py | 38 ++- erpnext/buying/doctype/supplier/supplier.js | 13 +- erpnext/controllers/accounts_controller.py | 275 +++++++++++------- erpnext/selling/doctype/customer/customer.js | 14 +- .../selling/doctype/customer/customer.json | 14 +- erpnext/setup/doctype/company/company.js | 3 +- erpnext/setup/doctype/company/company.json | 29 +- .../doctype/customer_group/customer_group.js | 13 +- .../doctype/supplier_group/supplier_group.js | 11 + 20 files changed, 385 insertions(+), 330 deletions(-) diff --git a/erpnext/accounts/doctype/party_account/party_account.json b/erpnext/accounts/doctype/party_account/party_account.json index 719b474f1f2..6ac6e560863 100644 --- a/erpnext/accounts/doctype/party_account/party_account.json +++ b/erpnext/accounts/doctype/party_account/party_account.json @@ -7,8 +7,7 @@ "field_order": [ "company", "account", - "advances_received_account", - "advances_paid_account" + "advance_account" ], "fields": [ { @@ -28,22 +27,16 @@ "options": "Account" }, { - "fieldname": "advances_received_account", + "fieldname": "advance_account", "fieldtype": "Link", - "label": "Advances Received Account", - "options": "Account" - }, - { - "fieldname": "advances_paid_account", - "fieldtype": "Link", - "label": "Advances Paid Account", + "label": "Advance Account", "options": "Account" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-06-02 13:00:06.885744", + "modified": "2023-06-05 14:15:42.053150", "modified_by": "Administrator", "module": "Accounts", "name": "Party Account", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 7bd3bb8710d..1f0e45fc110 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -18,20 +18,14 @@ frappe.ui.form.on('Payment Entry', { }, setup: function(frm) { - advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": frm.doc.company}, "book_advance_payments_as_liability"); - - if(advance_payments_as_liability && frm.doc.payment_type == 'Receive'){ - account_type = "Payable"; - } - else{ - account_type = "Receivable"; - } - frm.set_query("paid_from", function() { frm.events.validate_company(frm); + + var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ? + ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; return { filters: { - "account_type": account_type, + "account_type": ["in", account_types], "is_group": 0, "company": frm.doc.company } @@ -80,15 +74,12 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("paid_to", function() { frm.events.validate_company(frm); - if(advance_payments_as_liability && in_list(['Receive', 'Internal Transfer'], cur_frm.doc.payment_type)){ - account_type = ["Bank", "Cash"]; - } - else{ - account_type = "Receivable"; - } + + var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ? + ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; return { filters: { - "account_type": ["in", account_type], + "account_type": ["in", account_types], "is_group": 0, "company": frm.doc.company } @@ -279,25 +270,6 @@ frappe.ui.form.on('Payment Entry', { }, payment_type: function(frm) { - advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": frm.doc.company}, "book_advance_payments_as_liability"); - - if(advance_payments_as_liability && frm.doc.payment_type == 'Receive'){ - account_type = ["Payable"]; - } - else{ - account_type = ["Bank", "Cash"]; - } - - frm.set_query("paid_from", function() { - frm.events.validate_company(frm); - return { - filters: { - "account_type": ["in", account_type], - "is_group": 0, - "company": frm.doc.company - } - } - }); if(frm.doc.payment_type == "Internal Transfer") { $.each(["party", "party_balance", "paid_from", "paid_to", "references", "total_allocated_amount"], function(i, field) { @@ -364,7 +336,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_party_account_based_on_party = true; let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; - + return frappe.call({ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_details", args: { @@ -372,8 +344,7 @@ frappe.ui.form.on('Payment Entry', { party_type: frm.doc.party_type, party: frm.doc.party, date: frm.doc.posting_date, - cost_center: frm.doc.cost_center, - is_advance: !(frm.doc.references) + cost_center: frm.doc.cost_center }, callback: function(r, rt) { if(r.message) { @@ -741,7 +712,7 @@ frappe.ui.form.on('Payment Entry', { if(r.message) { var total_positive_outstanding = 0; var total_negative_outstanding = 0; - + console.log(r.message); $.each(r.message, function(i, d) { var c = frm.add_child("references"); c.reference_doctype = d.voucher_type; @@ -752,6 +723,7 @@ frappe.ui.form.on('Payment Entry', { c.bill_no = d.bill_no; c.payment_term = d.payment_term; c.allocated_amount = d.allocated_amount; + c.account = d.account; if(!in_list(frm.events.get_order_doctypes(frm), d.voucher_type)) { if(flt(d.outstanding_amount) > 0) @@ -1467,4 +1439,4 @@ frappe.ui.form.on('Payment Entry', { }); } }, -}) +}) \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 3927ecae43d..1c330b841ef 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -733,7 +733,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-02-14 04:52:30.478523", + "modified": "2023-06-07 14:36:50.521884", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index ed2158a6491..291f8e4411f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -86,12 +86,36 @@ class PaymentEntry(AccountsController): def on_submit(self): if self.difference_amount: frappe.throw(_("Difference Amount must be zero")) + book_advance_payments_as_liability = frappe.get_value( + "Company", {"company_name": self.company}, "book_advance_payments_as_liability" + ) + if book_advance_payments_as_liability: + self.get_liability_account() self.make_gl_entries() self.update_outstanding_amounts() self.update_advance_paid() self.update_payment_schedule() self.set_status() + def get_liability_account(self): + liability_account = get_party_account(self.party_type, self.party, self.company, is_advance=True) + if self.party_type == "Customer": + msg = "Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.".format( + frappe.bold(self.paid_from), + frappe.bold(liability_account), + ) + frappe.db.set_value("Payment Entry", self.name, "paid_from", liability_account) + self.paid_from = liability_account + else: + msg = "Book Advance Payments as Liability option is chosen. Paid To account changed from {0} to {1}.".format( + frappe.bold(self.paid_to), + frappe.bold(liability_account), + ) + frappe.db.set_value("Payment Entry", self.name, "paid_to", liability_account) + self.paid_to = liability_account + frappe.msgprint(_(msg), title="Warning", indicator="orange") + return liability_account + def on_cancel(self): self.ignore_linked_doctypes = ( "GL Entry", @@ -869,10 +893,14 @@ class PaymentEntry(AccountsController): if self.party_account: if self.payment_type == "Receive": against_account = self.paid_to + self.party_account = self.paid_from + dr_or_cr = "credit" else: against_account = self.paid_from + self.party_account = self.paid_to + dr_or_cr = "debit" - party_gl_dict = self.get_gl_dict( + party_dict = self.get_gl_dict( { "account": self.party_account, "party_type": self.party_type, @@ -883,30 +911,24 @@ class PaymentEntry(AccountsController): }, item=self, ) - - dr_or_cr = ( - "credit" if erpnext.get_party_account_type(self.party_type) == "Receivable" else "debit" - ) - for d in self.get("references"): - cost_center = self.cost_center - if d.reference_doctype == "Sales Invoice" and not cost_center: - cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center") - gle = party_gl_dict.copy() - gle.update( - { - "against_voucher_type": d.reference_doctype, - "against_voucher": d.reference_name, - "cost_center": cost_center, - } + book_advance_payments_as_liability = frappe.get_value( + "Company", {"company_name": self.company}, "book_advance_payments_as_liability" ) + if ( + d.reference_doctype in ["Sales Invoice", "Purchase Invoice"] + and book_advance_payments_as_liability + ): + self.make_invoice_liability_entry(gl_entries, d) allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) - + gle = party_dict.copy() gle.update( { - dr_or_cr + "_in_account_currency": d.allocated_amount, dr_or_cr: allocated_amount_in_company_currency, + dr_or_cr + "_in_account_currency": d.allocated_amount, + "against_voucher_type": d.reference_doctype, + "against_voucher": d.reference_name, } ) @@ -916,8 +938,7 @@ class PaymentEntry(AccountsController): exchange_rate = self.get_exchange_rate() base_unallocated_amount = self.unallocated_amount * exchange_rate - gle = party_gl_dict.copy() - + gle = party_dict.copy() gle.update( { dr_or_cr + "_in_account_currency": self.unallocated_amount, @@ -927,6 +948,40 @@ class PaymentEntry(AccountsController): gl_entries.append(gle) + def make_invoice_liability_entry(self, gl_entries, invoice): + args_dict = { + "party_type": self.party_type, + "party": self.party, + "account_currency": self.party_account_currency, + "cost_center": self.cost_center, + "voucher_type": invoice.reference_doctype, + "voucher_no": invoice.reference_name, + "against_voucher_type": invoice.reference_doctype, + "against_voucher": invoice.reference_name, + } + + dr_or_cr = "credit" if invoice.reference_doctype == "Sales Invoice" else "debit" + args_dict["account"] = invoice.account + args_dict[dr_or_cr] = invoice.allocated_amount + args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount + gle = self.get_gl_dict( + args_dict, + item=self, + ) + gl_entries.append(gle) + + args_dict[dr_or_cr] = 0 + args_dict[dr_or_cr + "_in_account_currency"] = 0 + dr_or_cr = "debit" if dr_or_cr == "credit" else "credit" + args_dict["account"] = self.get_liability_account() + args_dict[dr_or_cr] = invoice.allocated_amount + args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount + gle = self.get_gl_dict( + args_dict, + item=self, + ) + gl_entries.append(gle) + def add_bank_gl_entries(self, gl_entries): if self.payment_type in ("Pay", "Internal Transfer"): gl_entries.append( @@ -1401,6 +1456,7 @@ def split_invoices_based_on_payment_terms(outstanding_invoices): "outstanding_amount": flt(d.outstanding_amount), "payment_amount": payment_term.payment_amount, "payment_term": payment_term.payment_term, + "account": d.account, } ) ) @@ -1449,7 +1505,7 @@ def get_orders_to_be_billed( if voucher_type: doc = frappe.get_doc({"doctype": voucher_type}) condition = "" - if doc and hasattr(doc, "cost_center"): + if cost_center and doc and hasattr(doc, "cost_center"): condition = " and cost_center='%s'" % cost_center orders = [] @@ -1495,9 +1551,13 @@ def get_orders_to_be_billed( order_list = [] for d in orders: - if not ( - flt(d.outstanding_amount) >= flt(filters.get("outstanding_amt_greater_than")) - and flt(d.outstanding_amount) <= flt(filters.get("outstanding_amt_less_than")) + if filters.get("oustanding_amt_greater_than") and flt(d.outstanding_amount) < flt( + filters.get("outstanding_amt_greater_than") + ): + continue + + if filters.get("oustanding_amt_less_than") and flt(d.outstanding_amount) > flt( + filters.get("outstanding_amt_less_than") ): continue @@ -1519,6 +1579,7 @@ def get_negative_outstanding_invoices( condition=None, ): voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice" + account = "debit_to" if voucher_type == "Sales Invoice" else "credit_to" supplier_condition = "" if voucher_type == "Purchase Invoice": supplier_condition = "and (release_date is null or release_date <= CURRENT_DATE)" @@ -1532,7 +1593,7 @@ def get_negative_outstanding_invoices( return frappe.db.sql( """ select - "{voucher_type}" as voucher_type, name as voucher_no, + "{voucher_type}" as voucher_type, name as voucher_no, {account} as account, if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount, outstanding_amount, posting_date, due_date, conversion_rate as exchange_rate @@ -1555,6 +1616,7 @@ def get_negative_outstanding_invoices( "party_type": scrub(party_type), "party_account": "debit_to" if party_type == "Customer" else "credit_to", "cost_center": cost_center, + "account": account, } ), (party, party_account), @@ -1563,12 +1625,12 @@ def get_negative_outstanding_invoices( @frappe.whitelist() -def get_party_details(company, party_type, party, date, cost_center=None, is_advance=False): +def get_party_details(company, party_type, party, date, cost_center=None): bank_account = "" if not frappe.db.exists(party_type, party): frappe.throw(_("Invalid {0}: {1}").format(party_type, party)) - party_account = get_party_account(party_type, party, company, is_advance) + party_account = get_party_account(party_type, party, company) account_currency = get_account_currency(party_account) account_balance = get_balance_on(party_account, date, cost_center=cost_center) _party_name = "title" if party_type == "Shareholder" else party_type.lower() + "_name" diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json index 3003c68196e..c318ea53bdb 100644 --- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json +++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json @@ -15,7 +15,8 @@ "outstanding_amount", "allocated_amount", "exchange_rate", - "exchange_gain_loss" + "exchange_gain_loss", + "account" ], "fields": [ { @@ -101,12 +102,18 @@ "label": "Exchange Gain/Loss", "options": "Company:company:default_currency", "read_only": 1 + }, + { + "fieldname": "account", + "fieldtype": "Link", + "label": "Account", + "options": "Account" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-12-12 12:31:44.919895", + "modified": "2023-06-07 14:35:06.166907", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Reference", diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 2e9628c9f0a..d8743bb69e2 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -29,22 +29,12 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo }; }); - this.frm.set_query('default_advances_received_account', () => { + this.frm.set_query('default_advance_account', () => { return { filters: { "company": this.frm.doc.company, "is_group": 0, - "root_type": "Liability" - } - }; - }); - - this.frm.set_query('default_advances_paid_account', () => { - return { - filters: { - "company": this.frm.doc.company, - "is_group": 0, - "root_type": "Asset" + "root_type": (this.frm.party_type == 'Customer') ? "Liability": "Asset" } }; }); @@ -169,23 +159,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo }, callback: (r) => { if (!r.exc && r.message) { - this.frm.set_value("default_advances_received_account", r.message); - } - this.frm.refresh(); - } - }); - - frappe.call({ - method: "erpnext.accounts.party.get_party_account", - args: { - company: this.frm.doc.company, - party_type: (this.frm.doc.party_type == 'Customer')?'Supplier':'Customer', - party: this.frm.doc.party, - is_advance: 1 - }, - callback: (r) => { - if (!r.exc && r.message) { - this.frm.set_value("default_advances_paid_account", r.message); + this.frm.set_value("default_advance_account", r.message); } this.frm.refresh(); } diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index 21b8392d609..0e166ffd5d5 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -10,8 +10,7 @@ "party", "column_break_4", "receivable_payable_account", - "default_advances_received_account", - "default_advances_paid_account", + "default_advance_account", "col_break1", "from_invoice_date", "from_payment_date", @@ -190,27 +189,19 @@ }, { "depends_on": "eval:doc.party_type", - "fieldname": "default_advances_received_account", + "fieldname": "default_advance_account", "fieldtype": "Link", - "label": "Default Advances Received Account", + "label": "Default Advance Account", "mandatory_depends_on": "doc.party_type", "options": "Account", "reqd": 1 - }, - { - "depends_on": "eval:doc.party_type", - "fieldname": "default_advances_paid_account", - "fieldtype": "Link", - "label": "Default Advances Paid Account", - "mandatory_depends_on": "doc.party_type", - "options": "Account" } ], "hide_toolbar": 1, "icon": "icon-resize-horizontal", "issingle": 1, "links": [], - "modified": "2023-06-02 14:32:27.276083", + "modified": "2023-06-05 20:09:58.925427", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 9b03d36a8e9..e7d7f2c1e47 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -13,14 +13,17 @@ import erpnext from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation import ( is_any_doc_running, ) +from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.utils import ( QueryPaymentLedger, get_outstanding_invoices, reconcile_against_document, ) -from erpnext.controllers.accounts_controller import get_advance_payment_entries -from erpnext.controllers.accounts_controller import make_advance_liability_entry -from erpnext.accounts.general_ledger import make_gl_entries +from erpnext.controllers.accounts_controller import ( + get_advance_payment_entries, + make_advance_liability_entry, +) + class PaymentReconciliation(Document): def __init__(self, *args, **kwargs): @@ -57,9 +60,17 @@ class PaymentReconciliation(Document): self.add_payment_entries(non_reconciled_payments) def get_payment_entries(self): - receivable_payable_account = self.receivable_payable_account - default_advances_account = self.default_advances_received_account - party_account = [receivable_payable_account, default_advances_account] + advance_accounts = [] + if self.party_type == "Customer": + advance_accounts = frappe.db.get_list( + "Account", filters={"root_type": "Liability", "company": self.company}, pluck="name" + ) + elif self.party_type == "Supplier": + advance_accounts = frappe.db.get_list( + "Account", filters={"root_type": "Asset", "company": self.company}, pluck="name" + ) + party_account = [self.receivable_payable_account] + advance_accounts + order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" condition = frappe._dict( { @@ -69,12 +80,12 @@ class PaymentReconciliation(Document): "from_payment_date": self.get("from_payment_date"), "to_payment_date": self.get("to_payment_date"), "maximum_payment_amount": self.get("maximum_payment_amount"), - "minimum_payment_amount": self.get("minimum_payment_amount") + "minimum_payment_amount": self.get("minimum_payment_amount"), } ) payment_entries = get_advance_payment_entries( - self.party_type, + self.party_type, self.party, party_account, order_doctype, @@ -336,7 +347,9 @@ class PaymentReconciliation(Document): if row.invoice_number and row.allocated_amount: if row.invoice_type in ["Sales Invoice", "Purchase Invoice"]: gl_entries = [] - make_advance_liability_entry(gl_entries, row.reference_name, row.allocated_amount, row.invoice_number, self.party_type) + make_advance_liability_entry( + gl_entries, row.reference_name, row.allocated_amount, row.invoice_number, self.party_type + ) make_gl_entries(gl_entries) if row.reference_type in ["Sales Invoice", "Purchase Invoice"]: reconciled_entry = dr_or_cr_notes diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 60f9d62bf21..f0f1684b4d2 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1086,6 +1086,7 @@ "fieldtype": "Button", "label": "Get Advances Paid", "oldfieldtype": "Button", + "options": "set_advances", "print_hide": 1 }, { @@ -1364,12 +1365,12 @@ "depends_on": "eval:doc.update_stock && doc.is_internal_supplier", "fieldname": "set_from_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Set From Warehouse", "no_copy": 1, "options": "Warehouse", "print_hide": 1, "print_width": "50px", - "ignore_user_permissions": 1, "width": "50px" }, { @@ -1573,7 +1574,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2023-04-29 12:57:50.832598", + "modified": "2023-06-05 17:40:35.320635", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6051c9915d2..dbc277044a7 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3353,14 +3353,16 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): gl_entries = frappe.db.sql( """select account, debit, credit, posting_date from `tabGL Entry` - where voucher_type='Sales Invoice' and voucher_no=%s and posting_date > %s + where voucher_type='Sales Invoice' and voucher_no=%s and posting_date >= %s and is_cancelled = 0 order by posting_date asc, account asc""", (voucher_no, posting_date), as_dict=1, + debug=True, ) for i, gle in enumerate(gl_entries): + print(i, gle) doc.assertEqual(expected_gle[i][0], gle.account) doc.assertEqual(expected_gle[i][1], gle.debit) doc.assertEqual(expected_gle[i][2], gle.credit) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 3be42758885..782c41e529c 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -380,9 +380,7 @@ def get_party_account(party_type, party=None, company=None, is_advance=False): return frappe.get_cached_value("Company", company, default_account_name) - advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": company}, "book_advance_payments_as_liability") - - if is_advance and advance_payments_as_liability and party_type in ["Customer", "Supplier"]: + if is_advance and party_type in ["Customer", "Supplier"]: return get_party_advance_account(party_type, party, company) account = frappe.db.get_value( @@ -415,9 +413,10 @@ def get_party_account(party_type, party=None, company=None, is_advance=False): def get_party_advance_account(party_type, party, company): - account_name = 'advances_received_account' if party_type == 'Customer' else 'advances_paid_account' account = frappe.db.get_value( - "Party Account", {"parenttype": party_type, "parent": party, "company": company}, account_name + "Party Account", + {"parenttype": party_type, "parent": party, "company": company}, + "advance_account", ) if not account: @@ -426,14 +425,15 @@ def get_party_advance_account(party_type, party, company): account = frappe.db.get_value( "Party Account", {"parenttype": party_group_doctype, "parent": group, "company": company}, - account_name, + "advance_account", ) - + if not account: - account = frappe.get_cached_value("Company", company, "default_" + account_name) + account = frappe.get_cached_value("Company", company, "default_advance_account") return account - + + @frappe.whitelist() def get_party_bank_account(party_type, party): return frappe.db.get_value( diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 5abc64315cc..506279c1e50 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -498,30 +498,36 @@ def check_if_advance_entry_modified(args): journal_entry = frappe.qb.DocType("Journal Entry") journal_acc = frappe.qb.DocType("Journal Entry Account") - q = (frappe.qb.from_(journal_entry) + q = ( + frappe.qb.from_(journal_entry) .innerjoin(journal_acc) .on(journal_entry.name == journal_acc.parent) ) - if args.get("dr_or_cr") == 'debit_in_account_currency': + if args.get("dr_or_cr") == "debit_in_account_currency": q = q.select(journal_acc.debit_in_account_currency) else: q = q.select(journal_acc.credit_in_account_currency) - - q = q.where((journal_acc.account == args.get("account")) - &((journal_acc.party_type == args.get("party_type"))) - &((journal_acc.party == args.get("party"))) - &((journal_acc.reference_type == None) | (journal_acc.reference_type.isin(['', 'Sales Order', 'Purchase Order']))) - &((journal_entry.name == args.get("voucher_no"))) - &((journal_acc.name == args.get("voucher_detail_no"))) - &((journal_entry.docstatus == 1)) + + q = q.where( + (journal_acc.account == args.get("account")) + & ((journal_acc.party_type == args.get("party_type"))) + & ((journal_acc.party == args.get("party"))) + & ( + (journal_acc.reference_type == None) + | (journal_acc.reference_type.isin(["", "Sales Order", "Purchase Order"])) + ) + & ((journal_entry.name == args.get("voucher_no"))) + & ((journal_acc.name == args.get("voucher_detail_no"))) + & ((journal_entry.docstatus == 1)) ) else: payment_entry = frappe.qb.DocType("Payment Entry") payment_ref = frappe.qb.DocType("Payment Entry Reference") - q = (frappe.qb.from_(payment_entry) + q = ( + frappe.qb.from_(payment_entry) .select(payment_entry.name) .where(payment_entry.name == args.get("voucher_no")) .where(payment_entry.docstatus == 1) @@ -530,15 +536,16 @@ def check_if_advance_entry_modified(args): ) if args.voucher_detail_no: - q = ( q.inner_join(payment_ref) + q = ( + q.inner_join(payment_ref) .on(payment_entry.name == payment_ref.parent) .where(payment_ref.name == args.get("voucher_detail_no")) - .where(payment_ref.reference_doctype.isin(('', 'Sales Order', 'Purchase Order'))) + .where(payment_ref.reference_doctype.isin(("", "Sales Order", "Purchase Order"))) .where(payment_ref.allocated_amount == args.get("unreconciled_amount")) - ) + ) else: q = q.where(payment_entry.unallocated_amount == args.get("unreconciled_amount")) - + ret = q.run(as_dict=True) if not ret: @@ -921,6 +928,7 @@ def get_outstanding_invoices( "outstanding_amount": outstanding_amount, "due_date": d.due_date, "currency": d.currency, + "account": d.account, } ) ) diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 9e217b5113c..6da47c5c698 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -18,7 +18,7 @@ frappe.ui.form.on("Supplier", { } }); - frm.set_query('advances_received_account', 'accounts', function (doc, cdt, cdn) { + frm.set_query('advance_account', 'accounts', function (doc, cdt, cdn) { var d = locals[cdt][cdn]; return { filters: { @@ -29,17 +29,6 @@ frappe.ui.form.on("Supplier", { } }); - frm.set_query('advances_paid_account', 'accounts', function (doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "root_type": 'Liability', - "company": d.company, - "is_group": 0 - } - } - }); - frm.set_query("default_bank_account", function() { return { filters: { diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index ac32fd352ce..0589f4a89ac 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -7,9 +7,8 @@ import json import frappe from frappe import _, bold, throw from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied -from frappe.query_builder.functions import Abs, Sum from frappe.query_builder.custom import ConstantColumn - +from frappe.query_builder.functions import Abs, Sum from frappe.utils import ( add_days, add_months, @@ -859,7 +858,6 @@ class AccountsController(TransactionBase): amount = self.get("base_rounded_total") or self.base_grand_total else: amount = self.get("rounded_total") or self.grand_total - allocated_amount = min(amount - advance_allocated, d.amount) advance_allocated += flt(allocated_amount) @@ -887,16 +885,18 @@ class AccountsController(TransactionBase): amount_field = "credit_in_account_currency" order_field = "sales_order" order_doctype = "Sales Order" - party_account = frappe.db.get_values("Company", {"company_name": self.company}, ["default_receivable_account", "default_advances_received_account"]) + party_account = [ + get_party_account(party_type, party=party, company=self.company, is_advance=True) + ] else: party_type = "Supplier" party = self.supplier amount_field = "debit_in_account_currency" order_field = "purchase_order" order_doctype = "Purchase Order" - party_account = frappe.db.get_values("Company", {"company_name": self.company}, ["default_receivable_account", "default_advances_paid_account"]) - - party_account = list(party_account[0]) + party_account = [ + get_party_account(party_type, party=party, company=self.company, is_advance=True) + ] order_list = list(set(d.get(order_field) for d in self.get("items") if d.get(order_field))) @@ -2144,35 +2144,43 @@ def get_advance_journal_entries( order_list, include_unallocated=True, ): - journal_entry = frappe.qb.DocType('Journal Entry') - journal_acc = frappe.qb.DocType('Journal Entry Account') - q = (frappe.qb.from_(journal_entry) + journal_entry = frappe.qb.DocType("Journal Entry") + journal_acc = frappe.qb.DocType("Journal Entry Account") + q = ( + frappe.qb.from_(journal_entry) .inner_join(journal_acc) .on(journal_entry.name == journal_acc.parent) .select( - ConstantColumn('Journal Entry').as_('reference_type'), - (journal_entry.name).as_('reference_name'), - (journal_entry.remark).as_('remarks'), - (journal_acc.debit_in_account_currency if party_type == 'Supplier' else journal_acc.credit_in_account_currency).as_('amount'), - (journal_acc.name).as_('reference_row'), - (journal_acc.reference_name).as_('against_order'), - (journal_acc.exchange_rate) + ConstantColumn("Journal Entry").as_("reference_type"), + (journal_entry.name).as_("reference_name"), + (journal_entry.remark).as_("remarks"), + ( + journal_acc.debit_in_account_currency + if party_type == "Supplier" + else journal_acc.credit_in_account_currency + ).as_("amount"), + (journal_acc.name).as_("reference_row"), + (journal_acc.reference_name).as_("against_order"), + (journal_acc.exchange_rate), ) - .where(journal_acc.account.isin(party_account) - & (journal_acc.party_type == party_type) - & (journal_acc.party == party) - & (journal_acc.is_advance == 'Yes') - & (journal_entry.docstatus == 1) - ) - ) + .where( + journal_acc.account.isin(party_account) + & (journal_acc.party_type == party_type) + & (journal_acc.party == party) + & (journal_acc.is_advance == "Yes") + & (journal_entry.docstatus == 1) + ) + ) if party_type == "Customer": q = q.where(journal_acc.credit_in_account_currency > 0) + else: + q = q.where(journal_acc.debit_in_account_currency > 0) + if order_list: q = q.where(journal_acc.reference_type == order_doctype) if include_unallocated: - q = q.where(journal_acc.reference_name.isin(order_list) - |(journal_acc.reference_name == '')) + q = q.where(journal_acc.reference_name.isin(order_list) | (journal_acc.reference_name == "")) else: q = q.where(journal_acc.reference_name.isin(order_list)) @@ -2194,69 +2202,119 @@ def get_advance_payment_entries( condition=None, ): - q = build_query(party_type, party, party_account, order_doctype, order_list, include_unallocated, against_all_orders, limit, condition) + q = build_query( + party_type, + party, + party_account, + order_doctype, + order_list, + include_unallocated, + against_all_orders, + limit, + condition, + ) payment_entries = q.run(as_dict=True) return list(payment_entries) -def build_query(party_type, party, party_account, order_doctype, order_list, include_unallocated, against_all_orders, limit, condition): - payment_type = "Receive" if party_type == "Customer" else "Pay" - payment_entry = frappe.qb.DocType('Payment Entry') - payment_ref = frappe.qb.DocType('Payment Entry Reference') - q = (frappe.qb.from_(payment_entry) +def build_query( + party_type, + party, + party_account, + order_doctype, + order_list, + include_unallocated, + against_all_orders, + limit, + condition, +): + payment_type = "Receive" if party_type == "Customer" else "Pay" + payment_entry = frappe.qb.DocType("Payment Entry") + payment_ref = frappe.qb.DocType("Payment Entry Reference") + + q = ( + frappe.qb.from_(payment_entry) .select( - ConstantColumn('Payment Entry').as_('reference_type'), - (payment_entry.name).as_('reference_name'), + ConstantColumn("Payment Entry").as_("reference_type"), + (payment_entry.name).as_("reference_name"), payment_entry.posting_date, - (payment_entry.remarks).as_('remarks') + (payment_entry.remarks).as_("remarks"), ) .where(payment_entry.payment_type == payment_type) .where(payment_entry.party_type == party_type) .where(payment_entry.party == party) .where(payment_entry.docstatus == 1) ) - + if party_type == "Customer": - q = q.select(payment_entry.paid_from_account_currency) - q = q.select(payment_entry.paid_from) - q = q.where(payment_entry.paid_from.isin(party_account)) + q = q.select(payment_entry.paid_from_account_currency) + q = q.select(payment_entry.paid_from) + q = q.where(payment_entry.paid_from.isin(party_account)) else: q = q.select(payment_entry.paid_to_account_currency) q = q.select(payment_entry.paid_to) - q = q.where(payment_entry.paid_to.isin(party_account)) + q = q.where(payment_entry.paid_to.isin(party_account)) if payment_type == "Receive": q = q.select(payment_entry.source_exchange_rate) - else: + else: q.select(payment_entry.target_exchange_rate) if include_unallocated: - q = q.select((payment_entry.unallocated_amount).as_('amount')) - q = q.where(payment_entry.unallocated_amount>0) + q = q.select((payment_entry.unallocated_amount).as_("amount")) + q = q.where(payment_entry.unallocated_amount > 0) if condition: q = q.where(payment_entry.company == condition["company"]) - q = q.where(payment_entry.posting_date >= condition["from_payment_date"]) if condition.get("from_payment_date") else q - q = q.where(payment_entry.posting_date <= condition["to_payment_date"]) if condition.get("to_payment_date") else q + q = ( + q.where(payment_entry.posting_date >= condition["from_payment_date"]) + if condition.get("from_payment_date") + else q + ) + q = ( + q.where(payment_entry.posting_date <= condition["to_payment_date"]) + if condition.get("to_payment_date") + else q + ) if condition.get("get_payments") == True: - q = q.where(payment_entry.cost_center == condition["cost_center"]) if condition.get("cost_center") else q - q = q.where(payment_entry.unallocated_amount >= condition["minimum_payment_amount"]) if condition.get("minimum_payment_amount") else q - q = q.where(payment_entry.unallocated_amount <= condition["maximum_payment_amount"]) if condition.get("maximum_payment_amount") else q + q = ( + q.where(payment_entry.cost_center == condition["cost_center"]) + if condition.get("cost_center") + else q + ) + q = ( + q.where(payment_entry.unallocated_amount >= condition["minimum_payment_amount"]) + if condition.get("minimum_payment_amount") + else q + ) + q = ( + q.where(payment_entry.unallocated_amount <= condition["maximum_payment_amount"]) + if condition.get("maximum_payment_amount") + else q + ) else: - q = q.where(payment_entry.total_debit >= condition["minimum_payment_amount"]) if condition.get("minimum_payment_amount") else q - q = q.where(payment_entry.total_debit <= condition["maximum_payment_amount"]) if condition.get("maximum_payment_amount") else q + q = ( + q.where(payment_entry.total_debit >= condition["minimum_payment_amount"]) + if condition.get("minimum_payment_amount") + else q + ) + q = ( + q.where(payment_entry.total_debit <= condition["maximum_payment_amount"]) + if condition.get("maximum_payment_amount") + else q + ) elif order_list or against_all_orders: q = q.inner_join(payment_ref).on(payment_entry.name == payment_ref.parent) q = q.select( - (payment_ref.allocated_amount).as_('amount'), - (payment_ref.name).as_('reference_row'), - (payment_ref.reference_name).as_('against_order'), - payment_ref.reference_doctype == order_doctype + (payment_ref.allocated_amount).as_("amount"), + (payment_ref.name).as_("reference_row"), + (payment_ref.reference_name).as_("against_order"), + payment_ref.reference_doctype == order_doctype, ) - + if order_list: q = q.where(payment_ref.reference_name.isin(order_list)) @@ -2862,9 +2920,12 @@ def validate_regional(doc): def validate_einvoice_fields(doc): pass -def make_advance_liability_entry(gl_entries, pe, allocated_amount, invoice, party_type): + +def make_advance_liability_entry( + gl_entries, pe, allocated_amount, invoice, party_type, references=False +): pe = frappe.get_doc("Payment Entry", pe) - if party_type=="Customer": + if party_type == "Customer": invoice = frappe.get_doc("Sales Invoice", invoice) account = pe.paid_from dr_or_cr = "debit" @@ -2880,53 +2941,55 @@ def make_advance_liability_entry(gl_entries, pe, allocated_amount, invoice, part against = invoice.credit_to party = invoice.supplier voucher_type = "Purchase Invoice" - gl_entries.append(invoice.get_gl_dict( - { - "account": account, - "party_type": party_type, - "party": party, - "due_date": invoice.due_date, - "against": against, - dr_or_cr: allocated_amount, - dr_or_cr + "_in_account_currency": allocated_amount, - rev: 0, - rev + "_in_account_currency": 0, - "against_voucher": invoice.return_against - if cint(invoice.is_return) and invoice.return_against - else invoice.name, - "against_voucher_type": invoice.doctype, - "cost_center": invoice.cost_center, - "project": invoice.project, - "voucher_type": voucher_type, - "voucher_no": invoice.name - }, - invoice.party_account_currency, - item=invoice, - )) - - (dr_or_cr, rev) = ("credit", "debit") if party_type=="Customer" else ("debit", "credit") - gl_entries.append(invoice.get_gl_dict( - { - "account": against, - "party_type": party_type, - "party": party, - "due_date": invoice.due_date, - "against": account, - dr_or_cr: allocated_amount, - dr_or_cr + "_in_account_currency": allocated_amount, - rev: 0, - rev + "_in_account_currency": 0, - "against_voucher": invoice.return_against - if cint(invoice.is_return) and invoice.return_against - else invoice.name, - "against_voucher_type": invoice.doctype, - "cost_center": invoice.cost_center, - "project": invoice.project, - "voucher_type": voucher_type, - "voucher_no": invoice.name - }, - invoice.party_account_currency, - item=invoice, - )) - + gl_entries.append( + invoice.get_gl_dict( + { + "account": account, + "party_type": party_type, + "party": party, + "due_date": invoice.due_date, + "against": against, + dr_or_cr: allocated_amount, + dr_or_cr + "_in_account_currency": allocated_amount, + rev: 0, + rev + "_in_account_currency": 0, + "against_voucher": invoice.return_against + if cint(invoice.is_return) and invoice.return_against + else invoice.name, + "against_voucher_type": invoice.doctype, + "cost_center": invoice.cost_center, + "project": invoice.project, + "voucher_type": voucher_type, + "voucher_no": invoice.name, + }, + invoice.party_account_currency, + item=invoice, + ) + ) + (dr_or_cr, rev) = ("credit", "debit") if party_type == "Customer" else ("debit", "credit") + gl_entries.append( + invoice.get_gl_dict( + { + "account": against, + "party_type": party_type, + "party": party, + "due_date": invoice.due_date, + "against": account, + dr_or_cr: allocated_amount, + dr_or_cr + "_in_account_currency": allocated_amount, + rev: 0, + rev + "_in_account_currency": 0, + "against_voucher": invoice.return_against + if cint(invoice.is_return) and invoice.return_against + else invoice.name, + "against_voucher_type": invoice.doctype, + "cost_center": invoice.cost_center, + "project": invoice.project, + "voucher_type": "Payment Entry" if references else voucher_type, + "voucher_no": pe.name if references else invoice.name, + }, + invoice.party_account_currency, + item=invoice, + ) + ) diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 6dac6927471..35cf8c53ee2 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -34,8 +34,8 @@ frappe.ui.form.on("Customer", { filters: filters } }); - - frm.set_query('advances_received_account', 'accounts', function (doc, cdt, cdn) { + + frm.set_query('advance_account', 'accounts', function (doc, cdt, cdn) { var d = locals[cdt][cdn]; return { filters: { @@ -46,16 +46,6 @@ frappe.ui.form.on("Customer", { } }); - frm.set_query('advances_paid_account', 'accounts', function (doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "root_type": 'Asset', - "company": d.company, - "is_group": 0 - } - } - }); if (frm.doc.__islocal == 1) { frm.set_value("represents_company", ""); diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 0050279a4bd..46b10351fd8 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -337,12 +337,12 @@ "label": "Default Accounts" }, { - "description": "Mention if a non-standard receivable account", - "fieldname": "accounts", - "fieldtype": "Table", - "label": "Accounts", - "options": "Party Account" - }, + "description": "Mention if non-standard Receivable account", + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounts", + "options": "Party Account" + }, { "fieldname": "credit_limit_section", "fieldtype": "Section Break", @@ -568,7 +568,7 @@ "link_fieldname": "party" } ], - "modified": "2023-05-29 14:29:17.789578", + "modified": "2023-06-05 13:48:46.152659", "modified_by": "Administrator", "module": "Selling", "name": "Customer", diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 81919af4e1d..fb0ee7f07a5 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -227,8 +227,7 @@ erpnext.company.setup_queries = function(frm) { ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}], ["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}], ["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}], - ["default_advances_received_account", {"root_type": "Liability"}], - ["default_advances_paid_account", {"root_type": "Asset"}], + ["default_advance_account", {"root_type": ["in", ["Liability", "Asset"]]}], ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 369e139eef4..5b3d3bb0864 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -72,9 +72,8 @@ "default_finance_book", "advance_payments_section", "book_advance_payments_as_liability", - "default_advances_received_account", + "default_advance_account", "column_break_cui0", - "default_advances_paid_account", "auto_accounting_for_stock_settings", "enable_perpetual_inventory", "enable_provisional_accounting_for_non_stock_items", @@ -711,25 +710,17 @@ "fieldtype": "Section Break", "label": "Advance Payments" }, - { - "depends_on": "eval:doc.book_advance_payments_as_liability", - "fieldname": "default_advances_received_account", - "fieldtype": "Link", - "label": "Default Advances Received Account", - "mandatory_depends_on": "book_advance_payments_as_liability", - "options": "Account" - }, - { - "depends_on": "eval:doc.book_advance_payments_as_liability", - "fieldname": "default_advances_paid_account", - "fieldtype": "Link", - "label": "Default Advances Paid Account", - "mandatory_depends_on": "book_advance_payments_as_liability", - "options": "Account" - }, { "fieldname": "column_break_cui0", "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.book_advance_payments_as_liability", + "fieldname": "default_advance_account", + "fieldtype": "Link", + "label": "Default Account", + "mandatory_depends_on": "book_advance_payments_as_liability", + "options": "Account" } ], "icon": "fa fa-building", @@ -737,7 +728,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-06-02 13:11:41.939016", + "modified": "2023-06-05 14:12:37.946451", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js index ef556c774df..3a71b43ee80 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.js +++ b/erpnext/setup/doctype/customer_group/customer_group.js @@ -37,7 +37,7 @@ cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(d } } -cur_frm.fields_dict['accounts'].grid.get_field('advances_received_account').get_query = function(doc, cdt, cdn) { +cur_frm.fields_dict['accounts'].grid.get_field('advance_account').get_query = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; return { filters: { @@ -46,15 +46,4 @@ cur_frm.fields_dict['accounts'].grid.get_field('advances_received_account').get_ "is_group": 0 } } -} - -cur_frm.fields_dict['accounts'].grid.get_field('advances_paid_account').get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "root_type": 'Asset', - "company": d.company, - "is_group": 0 - } - } } \ No newline at end of file diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js index e75030d4414..58ab7fa9282 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group.js @@ -36,3 +36,14 @@ cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(d } }; }; + +cur_frm.fields_dict['accounts'].grid.get_field('advance_account').get_query = function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Asset', + "company": d.company, + "is_group": 0 + } + } +}; \ No newline at end of file From b65e58c1ae215215a73ad92e24791e9c6090cfa5 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 8 Jun 2023 18:15:37 +0530 Subject: [PATCH 03/29] test: add tests for advance liability entries Add Sales and Purchase Invoice Tests to check if GL entries and Outstanding Amount are generated correctly when advance entries are recorded as liability. Few changes to return value of added column in Payment Entry References. --- .../doctype/payment_entry/payment_entry.py | 8 ++- .../payment_entry_reference.json | 2 +- .../purchase_invoice/test_purchase_invoice.py | 60 +++++++++++++++--- .../sales_invoice/test_sales_invoice.py | 63 +++++++++++++++---- erpnext/accounts/utils.py | 1 + 5 files changed, 113 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 291f8e4411f..236a78a370a 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1741,6 +1741,9 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre if reference_doctype in ("Sales Invoice", "Purchase Invoice"): outstanding_amount = ref_doc.get("outstanding_amount") + account = ( + ref_doc.get("debit_to") if reference_doctype == "Sales Invoice" else ref_doc.get("credit_to") + ) else: outstanding_amount = flt(total_amount) - flt(ref_doc.get("advance_paid")) @@ -1748,7 +1751,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre # Get the exchange rate based on the posting date of the ref doc. exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date) - return frappe._dict( + res = frappe._dict( { "due_date": ref_doc.get("due_date"), "total_amount": flt(total_amount), @@ -1757,6 +1760,9 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre "bill_no": ref_doc.get("bill_no"), } ) + if account: + res.update({"account": account}) + return res @frappe.whitelist() diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json index c318ea53bdb..12aa0b520ec 100644 --- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json +++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json @@ -113,7 +113,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-06-07 14:35:06.166907", + "modified": "2023-06-08 07:40:38.487874", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Reference", diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index a6d7df6971f..ebb4970b3d9 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1662,22 +1662,66 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): self.assertTrue(return_pi.docstatus == 1) + def test_advance_entries_as_liability(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry + from erpnext.accounts.party import get_party_account + + frappe.db.set_value( + "Company", + "_Test Company", + {"book_advance_payments_as_liability": 1, "default_advance_account": "Debtors - _TC"}, + ) + pe = create_payment_entry( + company="_Test Company", + payment_type="Pay", + party_type="Supplier", + party="_Test Supplier", + paid_from="Cash - _TC", + paid_to=get_party_account("Supplier", "_Test Supplier", "_Test Company", is_advance=True), + paid_amount=1000, + ) + pe.submit() + + pi = make_purchase_invoice( + company="_Test Company", customer="_Test Supplier", do_not_save=True, do_not_submit=True + ) + pi.base_grand_total = 100 + pi.grand_total = 100 + pi.set_advances() + self.assertEqual(pi.advances[0].allocated_amount, 100) + pi.advances[0].allocated_amount = 50 + pi.advances = [pi.advances[0]] + pi.save() + pi.submit() + expected_gle = [ + ["Creditors - _TC", 50, 100], + ["Debtors - _TC", 0.0, 50], + ["Stock Received But Not Billed - _TC", 100, 0.0], + ] + + check_gl_entries(self, pi.name, expected_gle, nowdate()) + self.assertEqual(pi.outstanding_amount, 200) + def check_gl_entries(doc, voucher_no, expected_gle, posting_date): - gl_entries = frappe.db.sql( - """select account, debit, credit, posting_date - from `tabGL Entry` - where voucher_type='Purchase Invoice' and voucher_no=%s and posting_date >= %s - order by posting_date asc, account asc""", - (voucher_no, posting_date), - as_dict=1, + gl = frappe.qb.DocType("GL Entry") + q = ( + frappe.qb.from_(gl) + .select(gl.account, gl.debit, gl.credit, gl.posting_date) + .where( + (gl.voucher_type == "Sales Invoice") + & (gl.voucher_no == voucher_no) + & (gl.posting_date >= posting_date) + & (gl.is_cancelled == 0) + ) + .orderby(gl.posting_date, gl.account) ) + gl_entries = q.run(as_dict=True) for i, gle in enumerate(gl_entries): doc.assertEqual(expected_gle[i][0], gle.account) doc.assertEqual(expected_gle[i][1], gle.debit) doc.assertEqual(expected_gle[i][2], gle.credit) - doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) def create_tax_witholding_category(category_name, company, account): diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index dbc277044a7..d10fa05a190 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3313,6 +3313,46 @@ class TestSalesInvoice(unittest.TestCase): ) self.assertRaises(frappe.ValidationError, si.submit) + def test_advance_entries_as_liability(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry + from erpnext.accounts.party import get_party_account + + frappe.db.set_value( + "Company", + "_Test Company", + {"book_advance_payments_as_liability": 1, "default_advance_account": "Creditors - _TC"}, + ) + pe = create_payment_entry( + company="_Test Company", + payment_type="Receive", + party_type="Customer", + party="_Test Customer", + paid_from=get_party_account("Customer", "_Test Customer", "_Test Company", is_advance=True), + paid_to="Cash - _TC", + paid_amount=1000, + ) + pe.submit() + + si = create_sales_invoice( + company="_Test Company", customer="_Test Customer", do_not_save=True, do_not_submit=True + ) + si.base_grand_total = 100 + si.grand_total = 100 + si.set_advances() + self.assertEqual(si.advances[0].allocated_amount, 100) + si.advances[0].allocated_amount = 50 + si.advances = [si.advances[0]] + si.save() + si.submit() + expected_gle = [ + ["Creditors - _TC", 50, 0.0], + ["Debtors - _TC", 100, 50], + ["Sales - _TC", 0.0, 100], + ] + + check_gl_entries(self, si.name, expected_gle, nowdate()) + self.assertEqual(si.outstanding_amount, 50) + def get_sales_invoice_for_e_invoice(): si = make_sales_invoice_for_ewaybill() @@ -3350,23 +3390,24 @@ def get_sales_invoice_for_e_invoice(): def check_gl_entries(doc, voucher_no, expected_gle, posting_date): - gl_entries = frappe.db.sql( - """select account, debit, credit, posting_date - from `tabGL Entry` - where voucher_type='Sales Invoice' and voucher_no=%s and posting_date >= %s - and is_cancelled = 0 - order by posting_date asc, account asc""", - (voucher_no, posting_date), - as_dict=1, - debug=True, + gl = frappe.qb.DocType("GL Entry") + q = ( + frappe.qb.from_(gl) + .select(gl.account, gl.debit, gl.credit, gl.posting_date) + .where( + (gl.voucher_type == "Sales Invoice") + & (gl.voucher_no == voucher_no) + & (gl.posting_date >= posting_date) + & (gl.is_cancelled == 0) + ) + .orderby(gl.posting_date, gl.account) ) + gl_entries = q.run(as_dict=True) for i, gle in enumerate(gl_entries): - print(i, gle) doc.assertEqual(expected_gle[i][0], gle.account) doc.assertEqual(expected_gle[i][1], gle.debit) doc.assertEqual(expected_gle[i][2], gle.credit) - doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) def create_sales_invoice(**args): diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 506279c1e50..c1d365304fc 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -625,6 +625,7 @@ def update_reference_in_payment_entry( if not d.exchange_gain_loss else payment_entry.get_exchange_rate(), "exchange_gain_loss": d.exchange_gain_loss, # only populated from invoice in case of advance allocation + "account": d.account, } if d.voucher_detail_no: From 7591f1010b761adcb647f92988b3886bc9c6c40c Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 12 Jun 2023 11:06:03 +0530 Subject: [PATCH 04/29] fix: Make get party account method return a list instead of a single default account. --- .../doctype/party_account/party_account.json | 2 +- .../doctype/payment_entry/payment_entry.js | 1 - .../doctype/payment_entry/payment_entry.py | 61 ++++++++----------- .../payment_reconciliation.js | 20 +----- .../payment_reconciliation.json | 9 ++- .../purchase_invoice/test_purchase_invoice.py | 2 +- .../sales_invoice/test_sales_invoice.py | 2 +- erpnext/accounts/party.py | 8 +-- erpnext/buying/doctype/supplier/supplier.js | 4 +- erpnext/controllers/accounts_controller.py | 9 +-- erpnext/selling/doctype/customer/customer.js | 6 +- .../doctype/customer_group/customer_group.js | 57 ++++++++--------- .../doctype/supplier_group/supplier_group.js | 57 ++++++++--------- 13 files changed, 106 insertions(+), 132 deletions(-) diff --git a/erpnext/accounts/doctype/party_account/party_account.json b/erpnext/accounts/doctype/party_account/party_account.json index 6ac6e560863..7e345d84ead 100644 --- a/erpnext/accounts/doctype/party_account/party_account.json +++ b/erpnext/accounts/doctype/party_account/party_account.json @@ -36,7 +36,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-06-05 14:15:42.053150", + "modified": "2023-06-06 14:15:42.053150", "modified_by": "Administrator", "module": "Accounts", "name": "Party Account", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 1f0e45fc110..4cebb7b2fcd 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -712,7 +712,6 @@ frappe.ui.form.on('Payment Entry', { if(r.message) { var total_positive_outstanding = 0; var total_negative_outstanding = 0; - console.log(r.message); $.each(r.message, function(i, d) { var c = frm.add_child("references"); c.reference_doctype = d.voucher_type; diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 236a78a370a..5249f171fb8 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -60,6 +60,7 @@ class PaymentEntry(AccountsController): def validate(self): self.setup_party_account_field() self.set_missing_values() + self.set_liability_account() self.set_missing_ref_details() self.validate_payment_type() self.validate_party_details() @@ -86,35 +87,34 @@ class PaymentEntry(AccountsController): def on_submit(self): if self.difference_amount: frappe.throw(_("Difference Amount must be zero")) - book_advance_payments_as_liability = frappe.get_value( - "Company", {"company_name": self.company}, "book_advance_payments_as_liability" - ) - if book_advance_payments_as_liability: - self.get_liability_account() self.make_gl_entries() self.update_outstanding_amounts() self.update_advance_paid() self.update_payment_schedule() self.set_status() - def get_liability_account(self): - liability_account = get_party_account(self.party_type, self.party, self.company, is_advance=True) - if self.party_type == "Customer": - msg = "Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.".format( - frappe.bold(self.paid_from), - frappe.bold(liability_account), - ) - frappe.db.set_value("Payment Entry", self.name, "paid_from", liability_account) - self.paid_from = liability_account - else: - msg = "Book Advance Payments as Liability option is chosen. Paid To account changed from {0} to {1}.".format( - frappe.bold(self.paid_to), - frappe.bold(liability_account), - ) - frappe.db.set_value("Payment Entry", self.name, "paid_to", liability_account) - self.paid_to = liability_account - frappe.msgprint(_(msg), title="Warning", indicator="orange") - return liability_account + def set_liability_account(self): + book_advance_payments_as_liability = frappe.get_value( + "Company", {"company_name": self.company}, "book_advance_payments_as_liability" + ) + if not book_advance_payments_as_liability: + return + root_type = frappe.get_value( + "Account", {"name": self.party_account, "company": self.company}, "root_type" + ) + if (root_type == "Liability" and self.party_type == "Customer") or ( + root_type == "Asset" and self.party_type == "Supplier" + ): + return + liability_account = get_party_account( + self.party_type, self.party, self.company, include_advance=True + )[1] + self.set(self.party_account_field, liability_account) + msg = "Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.".format( + frappe.bold(self.party_account), + frappe.bold(liability_account), + ) + frappe.msgprint(_(msg), alert=True) def on_cancel(self): self.ignore_linked_doctypes = ( @@ -354,13 +354,6 @@ class PaymentEntry(AccountsController): elif self.party_type == "Employee": ref_party_account = ref_doc.payable_account - if ref_party_account != self.party_account: - frappe.throw( - _("{0} {1} is associated with {2}, but Party Account is {3}").format( - d.reference_doctype, d.reference_name, ref_party_account, self.party_account - ) - ) - if ref_doc.doctype == "Purchase Invoice" and ref_doc.get("on_hold"): frappe.throw( _("{0} {1} is on hold").format(d.reference_doctype, d.reference_name), @@ -893,11 +886,9 @@ class PaymentEntry(AccountsController): if self.party_account: if self.payment_type == "Receive": against_account = self.paid_to - self.party_account = self.paid_from dr_or_cr = "credit" else: against_account = self.paid_from - self.party_account = self.paid_to dr_or_cr = "debit" party_dict = self.get_gl_dict( @@ -927,8 +918,8 @@ class PaymentEntry(AccountsController): { dr_or_cr: allocated_amount_in_company_currency, dr_or_cr + "_in_account_currency": d.allocated_amount, - "against_voucher_type": d.reference_doctype, - "against_voucher": d.reference_name, + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, } ) @@ -973,7 +964,7 @@ class PaymentEntry(AccountsController): args_dict[dr_or_cr] = 0 args_dict[dr_or_cr + "_in_account_currency"] = 0 dr_or_cr = "debit" if dr_or_cr == "credit" else "credit" - args_dict["account"] = self.get_liability_account() + args_dict["account"] = self.party_account args_dict[dr_or_cr] = invoice.allocated_amount args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount gle = self.get_gl_dict( diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index d8743bb69e2..a40e6f274f6 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -134,32 +134,18 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo this.frm.trigger("clear_child_tables"); if (!this.frm.doc.receivable_payable_account && this.frm.doc.party_type && this.frm.doc.party) { - frappe.call({ - method: "erpnext.accounts.party.get_party_account", - args: { - company: this.frm.doc.company, - party_type: this.frm.doc.party_type, - party: this.frm.doc.party - }, - callback: (r) => { - if (!r.exc && r.message) { - this.frm.set_value("receivable_payable_account", r.message); - } - this.frm.refresh(); - } - }); - frappe.call({ method: "erpnext.accounts.party.get_party_account", args: { company: this.frm.doc.company, party_type: this.frm.doc.party_type, party: this.frm.doc.party, - is_advance: 1 + include_advance: 1 }, callback: (r) => { if (!r.exc && r.message) { - this.frm.set_value("default_advance_account", r.message); + this.frm.set_value("receivable_payable_account", r.message[0]); + this.frm.set_value("default_advance_account", r.message[1]); } this.frm.refresh(); } diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index 0e166ffd5d5..5f6c7034ed3 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -7,8 +7,8 @@ "field_order": [ "company", "party_type", - "party", "column_break_4", + "party", "receivable_payable_account", "default_advance_account", "col_break1", @@ -188,20 +188,19 @@ "options": "Cost Center" }, { - "depends_on": "eval:doc.party_type", + "depends_on": "eval:doc.party", "fieldname": "default_advance_account", "fieldtype": "Link", "label": "Default Advance Account", "mandatory_depends_on": "doc.party_type", - "options": "Account", - "reqd": 1 + "options": "Account" } ], "hide_toolbar": 1, "icon": "icon-resize-horizontal", "issingle": 1, "links": [], - "modified": "2023-06-05 20:09:58.925427", + "modified": "2023-06-09 13:02:48.718362", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index ebb4970b3d9..9ade46e045b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1677,7 +1677,7 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): party_type="Supplier", party="_Test Supplier", paid_from="Cash - _TC", - paid_to=get_party_account("Supplier", "_Test Supplier", "_Test Company", is_advance=True), + paid_to="Creditors - _TC", paid_amount=1000, ) pe.submit() diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index d10fa05a190..364bf6898a9 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3327,7 +3327,7 @@ class TestSalesInvoice(unittest.TestCase): payment_type="Receive", party_type="Customer", party="_Test Customer", - paid_from=get_party_account("Customer", "_Test Customer", "_Test Company", is_advance=True), + paid_from="Debtors - _TC", paid_to="Cash - _TC", paid_amount=1000, ) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 782c41e529c..ccd54b344fd 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -365,7 +365,7 @@ def set_account_and_due_date( @frappe.whitelist() -def get_party_account(party_type, party=None, company=None, is_advance=False): +def get_party_account(party_type, party=None, company=None, include_advance=False): """Returns the account for the given `party`. Will first search in party (Customer / Supplier) record, if not found, will search in group (Customer Group / Supplier Group), @@ -380,9 +380,6 @@ def get_party_account(party_type, party=None, company=None, is_advance=False): return frappe.get_cached_value("Company", company, default_account_name) - if is_advance and party_type in ["Customer", "Supplier"]: - return get_party_advance_account(party_type, party, company) - account = frappe.db.get_value( "Party Account", {"parenttype": party_type, "parent": party, "company": company}, "account" ) @@ -409,6 +406,9 @@ def get_party_account(party_type, party=None, company=None, is_advance=False): if (account and account_currency != existing_gle_currency) or not account: account = get_party_gle_account(party_type, party, company) + if include_advance and party_type in ["Customer", "Supplier"]: + advance_account = get_party_advance_account(party_type, party, company) + return [account, advance_account] return account diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 6da47c5c698..50f9bb6cb9d 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -8,7 +8,7 @@ frappe.ui.form.on("Supplier", { frm.set_value("represents_company", ""); } frm.set_query('account', 'accounts', function (doc, cdt, cdn) { - var d = locals[cdt][cdn]; + let d = locals[cdt][cdn]; return { filters: { 'account_type': 'Payable', @@ -19,7 +19,7 @@ frappe.ui.form.on("Supplier", { }); frm.set_query('advance_account', 'accounts', function (doc, cdt, cdn) { - var d = locals[cdt][cdn]; + let d = locals[cdt][cdn]; return { filters: { "root_type": 'Asset', diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 0589f4a89ac..d8c41352926 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -885,18 +885,15 @@ class AccountsController(TransactionBase): amount_field = "credit_in_account_currency" order_field = "sales_order" order_doctype = "Sales Order" - party_account = [ - get_party_account(party_type, party=party, company=self.company, is_advance=True) - ] else: party_type = "Supplier" party = self.supplier amount_field = "debit_in_account_currency" order_field = "purchase_order" order_doctype = "Purchase Order" - party_account = [ - get_party_account(party_type, party=party, company=self.company, is_advance=True) - ] + party_account = [ + get_party_account(party_type, party=party, company=self.company, include_advance=True)[1] + ] order_list = list(set(d.get(order_field) for d in self.get("items") if d.get(order_field))) diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 35cf8c53ee2..8f828807a59 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -20,8 +20,8 @@ frappe.ui.form.on("Customer", { frm.set_query('customer_group', {'is_group': 0}); frm.set_query('default_price_list', { 'selling': 1}); frm.set_query('account', 'accounts', function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - var filters = { + let d = locals[cdt][cdn]; + let filters = { 'account_type': 'Receivable', 'company': d.company, "is_group": 0 @@ -36,7 +36,7 @@ frappe.ui.form.on("Customer", { }); frm.set_query('advance_account', 'accounts', function (doc, cdt, cdn) { - var d = locals[cdt][cdn]; + let d = locals[cdt][cdn]; return { filters: { "root_type": 'Liability', diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js index 3a71b43ee80..ed9893346cb 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.js +++ b/erpnext/setup/doctype/customer_group/customer_group.js @@ -16,34 +16,35 @@ cur_frm.cscript.set_root_readonly = function(doc) { } } -//get query select Customer Group -cur_frm.fields_dict['parent_customer_group'].get_query = function(doc,cdt,cdn) { - return { - filters: { - 'is_group': 1, - 'name': ['!=', cur_frm.doc.customer_group_name] - } - } -} +frappe.ui.form.on("Customer Group", { + setup: function(frm){ + frm.set_query('parent_customer_group', function (doc) { + return { + filters: { + 'is_group': 1, + 'name': ['!=', cur_frm.doc.customer_group_name] + } + } + }); -cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "account_type": 'Receivable', - "company": d.company, - "is_group": 0 - } - } -} + frm.set_query('account', 'accounts', function (doc, cdt, cdn) { + return { + filters: { + "account_type": 'Receivable', + "company": locals[cdt][cdn].company, + "is_group": 0 + } + } + }); -cur_frm.fields_dict['accounts'].grid.get_field('advance_account').get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "root_type": 'Liability', - "company": d.company, - "is_group": 0 - } + frm.set_query('advance_account', 'accounts', function (doc, cdt, cdn) { + return { + filters: { + "root_type": 'Liability', + "company": locals[cdt][cdn].company, + "is_group": 0 + } + } + }); } -} \ No newline at end of file +}); diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js index 58ab7fa9282..ac5904f4d27 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group.js @@ -16,34 +16,35 @@ cur_frm.cscript.set_root_readonly = function(doc) { } }; -// get query select Customer Group -cur_frm.fields_dict['parent_supplier_group'].get_query = function() { - return { - filters: { - 'is_group': 1, - 'name': ['!=', cur_frm.doc.supplier_group_name] - } - }; -}; +frappe.ui.form.on("Supplier Group", { + setup: function(frm){ + frm.set_query('parent_supplier_group', function (doc) { + return { + filters: { + 'is_group': 1, + 'name': ['!=', cur_frm.doc.supplier_group_name] + } + } + }); -cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - 'account_type': 'Payable', - 'company': d.company, - "is_group": 0 - } - }; -}; + frm.set_query('account', 'accounts', function (doc, cdt, cdn) { + return { + filters: { + 'account_type': 'Payable', + 'company': locals[cdt][cdn].company, + "is_group": 0 + } + } + }); -cur_frm.fields_dict['accounts'].grid.get_field('advance_account').get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "root_type": 'Asset', - "company": d.company, - "is_group": 0 - } + frm.set_query('advance_account', 'accounts', function (doc, cdt, cdn) { + return { + filters: { + "root_type": 'Asset', + "company": locals[cdt][cdn].company, + "is_group": 0 + } + } + }); } -}; \ No newline at end of file +}); From a06017c2c3b0db520c86229619cd1c173d0a234c Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 12 Jun 2023 15:24:53 +0530 Subject: [PATCH 05/29] fix: Use advance account from Reconciliation document for fetching Payment Entries --- .../doctype/payment_entry/payment_entry.py | 8 +++- .../payment_reconciliation.js | 2 +- .../payment_reconciliation.py | 11 +---- .../purchase_invoice/purchase_invoice.py | 19 +++++---- .../purchase_invoice/test_purchase_invoice.py | 3 +- .../doctype/sales_invoice/sales_invoice.py | 19 +++++---- .../sales_invoice/test_sales_invoice.py | 6 ++- erpnext/accounts/party.py | 7 +++- erpnext/controllers/accounts_controller.py | 40 ++++++++++++++----- erpnext/setup/doctype/company/company.js | 3 +- erpnext/setup/doctype/company/company.json | 17 ++++++-- 11 files changed, 91 insertions(+), 44 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 5249f171fb8..7012eacb61e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -106,6 +106,12 @@ class PaymentEntry(AccountsController): root_type == "Asset" and self.party_type == "Supplier" ): return + if self.unallocated_amount == 0: + for d in self.references: + if d.reference_doctype in ["Sales Order", "Purchase Order"]: + break + else: + return liability_account = get_party_account( self.party_type, self.party, self.company, include_advance=True )[1] @@ -1694,7 +1700,7 @@ def get_outstanding_on_journal_entry(name): @frappe.whitelist() def get_reference_details(reference_doctype, reference_name, party_account_currency): - total_amount = outstanding_amount = exchange_rate = None + total_amount = outstanding_amount = exchange_rate = account = None ref_doc = frappe.get_doc(reference_doctype, reference_name) company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency( diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index a40e6f274f6..e46c81b9edc 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -34,7 +34,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo filters: { "company": this.frm.doc.company, "is_group": 0, - "root_type": (this.frm.party_type == 'Customer') ? "Liability": "Asset" + "root_type": this.frm.doc.party_type == 'Customer' ? "Liability": "Asset" } }; }); diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index e7d7f2c1e47..e5fb3df1a4e 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -60,16 +60,7 @@ class PaymentReconciliation(Document): self.add_payment_entries(non_reconciled_payments) def get_payment_entries(self): - advance_accounts = [] - if self.party_type == "Customer": - advance_accounts = frappe.db.get_list( - "Account", filters={"root_type": "Liability", "company": self.company}, pluck="name" - ) - elif self.party_type == "Supplier": - advance_accounts = frappe.db.get_list( - "Account", filters={"root_type": "Asset", "company": self.company}, pluck="name" - ) - party_account = [self.receivable_payable_account] + advance_accounts + party_account = [self.receivable_payable_account, self.default_advance_account] order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" condition = frappe._dict( diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index dfea8e9049d..0f3b12b3ef2 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -33,8 +33,10 @@ from erpnext.accounts.utils import get_account_currency, get_fiscal_year from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.buying.utils import check_on_hold_or_closed_status -from erpnext.controllers.accounts_controller import validate_account_head -from erpnext.controllers.accounts_controller import make_advance_liability_entry +from erpnext.controllers.accounts_controller import ( + check_advance_liability_entry, + validate_account_head, +) from erpnext.controllers.buying_controller import BuyingController from erpnext.stock import get_warehouse_account_map from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( @@ -581,11 +583,14 @@ class PurchaseInvoice(BuyingController): gl_entries = [] self.make_supplier_gl_entry(gl_entries) - - advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": self.company}, "book_advance_payments_as_liability") - if advance_payments_as_liability: - for advance_entry in self.advances: - make_advance_liability_entry(gl_entries, advance_entry.reference_name, advance_entry.allocated_amount, invoice=self.name, party_type="Supplier") + + check_advance_liability_entry( + gl_entries, + company=self.company, + advances=self.advances, + invoice=self.name, + party_type="Supplier", + ) self.make_item_gl_entries(gl_entries) self.make_precision_loss_gl_entry(gl_entries) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 9ade46e045b..c15692b751b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1669,7 +1669,7 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): frappe.db.set_value( "Company", "_Test Company", - {"book_advance_payments_as_liability": 1, "default_advance_account": "Debtors - _TC"}, + {"book_advance_payments_as_liability": 1, "default_advance_paid_account": "Debtors - _TC"}, ) pe = create_payment_entry( company="_Test Company", @@ -1722,6 +1722,7 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): doc.assertEqual(expected_gle[i][0], gle.account) doc.assertEqual(expected_gle[i][1], gle.debit) doc.assertEqual(expected_gle[i][2], gle.credit) + doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) def create_tax_witholding_category(category_name, company, account): diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 419628eaffc..d43e6e8c59e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -32,8 +32,10 @@ from erpnext.assets.doctype.asset.depreciation import ( reset_depreciation_schedule, reverse_depreciation_entry_made_after_disposal, ) -from erpnext.controllers.accounts_controller import validate_account_head -from erpnext.controllers.accounts_controller import make_advance_liability_entry +from erpnext.controllers.accounts_controller import ( + check_advance_liability_entry, + validate_account_head, +) from erpnext.controllers.selling_controller import SellingController from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data from erpnext.setup.doctype.company.company import update_company_current_month_sales @@ -1065,11 +1067,14 @@ class SalesInvoice(SellingController): gl_entries = [] self.make_customer_gl_entry(gl_entries) - - advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": self.company}, "book_advance_payments_as_liability") - if advance_payments_as_liability: - for advance_entry in self.advances: - make_advance_liability_entry(gl_entries, advance_entry.reference_name, advance_entry.allocated_amount, invoice=self.name, party_type="Customer") + + check_advance_liability_entry( + gl_entries, + company=self.company, + advances=self.advances, + invoice=self.name, + party_type="Customer", + ) self.make_tax_gl_entries(gl_entries) self.make_exchange_gain_loss_gl_entries(gl_entries) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 364bf6898a9..54c8b8472e2 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3320,7 +3320,10 @@ class TestSalesInvoice(unittest.TestCase): frappe.db.set_value( "Company", "_Test Company", - {"book_advance_payments_as_liability": 1, "default_advance_account": "Creditors - _TC"}, + { + "book_advance_payments_as_liability": 1, + "default_advance_received_account": "Creditors - _TC", + }, ) pe = create_payment_entry( company="_Test Company", @@ -3408,6 +3411,7 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): doc.assertEqual(expected_gle[i][0], gle.account) doc.assertEqual(expected_gle[i][1], gle.debit) doc.assertEqual(expected_gle[i][2], gle.credit) + doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) def create_sales_invoice(**args): diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index ccd54b344fd..1e38217a167 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -429,7 +429,12 @@ def get_party_advance_account(party_type, party, company): ) if not account: - account = frappe.get_cached_value("Company", company, "default_advance_account") + account_name = ( + "default_advance_received_account" + if party_type == "Customer" + else "default_advance_paid_account" + ) + account = frappe.get_cached_value("Company", company, account_name) return account diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index d8c41352926..3fac56cfa2b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2908,16 +2908,6 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.create_stock_reservation_entries() -@erpnext.allow_regional -def validate_regional(doc): - pass - - -@erpnext.allow_regional -def validate_einvoice_fields(doc): - pass - - def make_advance_liability_entry( gl_entries, pe, allocated_amount, invoice, party_type, references=False ): @@ -2990,3 +2980,33 @@ def make_advance_liability_entry( item=invoice, ) ) + + +def check_advance_liability_entry(gl_entries, company, advances, invoice, party_type): + advance_payments_as_liability = frappe.db.get_value( + "Company", {"company_name": company}, "book_advance_payments_as_liability" + ) + if advance_payments_as_liability: + for advance_entry in advances: + make_advance_liability_entry( + gl_entries, + advance_entry.reference_name, + advance_entry.allocated_amount, + invoice=invoice, + party_type=party_type, + ) + + +@erpnext.allow_regional +def validate_regional(doc): + pass + + +@erpnext.allow_regional +def validate_einvoice_fields(doc): + pass + + +@erpnext.allow_regional +def update_gl_dict_with_regional_fields(doc, gl_dict): + pass diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index fb0ee7f07a5..436fe0595a6 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -227,7 +227,8 @@ erpnext.company.setup_queries = function(frm) { ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}], ["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}], ["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}], - ["default_advance_account", {"root_type": ["in", ["Liability", "Asset"]]}], + ["default_advance_received_account", {"root_type": "Liability"}], + ["default_advance_paid_account", {"root_type": "Asset"}], ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 5b3d3bb0864..3523af1988f 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -72,7 +72,8 @@ "default_finance_book", "advance_payments_section", "book_advance_payments_as_liability", - "default_advance_account", + "default_advance_received_account", + "default_advance_paid_account", "column_break_cui0", "auto_accounting_for_stock_settings", "enable_perpetual_inventory", @@ -716,9 +717,17 @@ }, { "depends_on": "eval:doc.book_advance_payments_as_liability", - "fieldname": "default_advance_account", + "fieldname": "default_advance_received_account", "fieldtype": "Link", - "label": "Default Account", + "label": "Default Advance Received Account", + "mandatory_depends_on": "book_advance_payments_as_liability", + "options": "Account" + }, + { + "depends_on": "eval:doc.book_advance_payments_as_liability", + "fieldname": "default_advance_paid_account", + "fieldtype": "Link", + "label": "Default Advance Paid Account", "mandatory_depends_on": "book_advance_payments_as_liability", "options": "Account" } @@ -728,7 +737,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-06-05 14:12:37.946451", + "modified": "2023-06-12 12:51:12.007410", "modified_by": "Administrator", "module": "Setup", "name": "Company", From 5e9821dce24fdbca59a11f9763b8b96df2b0d3bd Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 12 Jun 2023 18:00:15 +0530 Subject: [PATCH 06/29] test: modify test to check posting date --- .../doctype/payment_entry/payment_entry.py | 19 ++++++++++++++++--- .../purchase_invoice/test_purchase_invoice.py | 11 ++++++++--- .../sales_invoice/test_sales_invoice.py | 14 +++++++++++--- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7012eacb61e..7a31dc58858 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -909,6 +909,7 @@ class PaymentEntry(AccountsController): item=self, ) for d in self.get("references"): + gle = party_dict.copy() book_advance_payments_as_liability = frappe.get_value( "Company", {"company_name": self.company}, "book_advance_payments_as_liability" ) @@ -917,17 +918,27 @@ class PaymentEntry(AccountsController): and book_advance_payments_as_liability ): self.make_invoice_liability_entry(gl_entries, d) + gle.update( + { + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, + } + ) allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) - gle = party_dict.copy() gle.update( { dr_or_cr: allocated_amount_in_company_currency, dr_or_cr + "_in_account_currency": d.allocated_amount, - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, } ) + if not gle.get("against_voucher_type"): + gle.update( + { + "against_voucher_type": d.reference_doctype, + "against_voucher": d.reference_name, + } + ) gl_entries.append(gle) @@ -940,6 +951,8 @@ class PaymentEntry(AccountsController): { dr_or_cr + "_in_account_currency": self.unallocated_amount, dr_or_cr: base_unallocated_amount, + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, } ) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index c15692b751b..1ac231bc19f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1694,13 +1694,18 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): pi.save() pi.submit() expected_gle = [ - ["Creditors - _TC", 50, 100], - ["Debtors - _TC", 0.0, 50], - ["Stock Received But Not Billed - _TC", 100, 0.0], + ["Creditors - _TC", 50, 100, nowdate()], + ["Debtors - _TC", 0.0, 50, nowdate()], + ["Stock Received But Not Billed - _TC", 100, 0.0, nowdate()], ] check_gl_entries(self, pi.name, expected_gle, nowdate()) self.assertEqual(pi.outstanding_amount, 200) + frappe.db.set_value( + "Company", + "_Test Company", + {"book_advance_payments_as_liability": 0, "default_advance_paid_account": ""}, + ) def check_gl_entries(doc, voucher_no, expected_gle, posting_date): diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 54c8b8472e2..8ab7fd7cd0c 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3348,13 +3348,21 @@ class TestSalesInvoice(unittest.TestCase): si.save() si.submit() expected_gle = [ - ["Creditors - _TC", 50, 0.0], - ["Debtors - _TC", 100, 50], - ["Sales - _TC", 0.0, 100], + ["Creditors - _TC", 50, 0.0, nowdate()], + ["Debtors - _TC", 100, 50, nowdate()], + ["Sales - _TC", 0.0, 100, nowdate()], ] check_gl_entries(self, si.name, expected_gle, nowdate()) self.assertEqual(si.outstanding_amount, 50) + frappe.db.set_value( + "Company", + "_Test Company", + { + "book_advance_payments_as_liability": 0, + "default_advance_received_account": "", + }, + ) def get_sales_invoice_for_e_invoice(): From 17341adf1cb385e3326552d95dc1bcd00e4b938a Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Tue, 13 Jun 2023 15:00:46 +0530 Subject: [PATCH 07/29] fix: calculate outstanding amount on reconcile correctly --- .../doctype/payment_entry/payment_entry.py | 21 ++++--- .../purchase_invoice/test_purchase_invoice.py | 58 ++++++++++------- .../sales_invoice/test_sales_invoice.py | 62 ++++++++++--------- erpnext/accounts/utils.py | 2 +- erpnext/buying/doctype/supplier/supplier.js | 2 +- erpnext/controllers/accounts_controller.py | 6 +- erpnext/selling/doctype/customer/customer.js | 2 +- erpnext/setup/doctype/company/company.js | 4 +- 8 files changed, 85 insertions(+), 72 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7a31dc58858..dd3490936ff 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -872,12 +872,12 @@ class PaymentEntry(AccountsController): self.set("remarks", "\n".join(remarks)) - def build_gl_map(self): + def build_gl_map(self, is_reconcile=False): if self.payment_type in ("Receive", "Pay") and not self.get("party_account_field"): self.setup_party_account_field() gl_entries = [] - self.add_party_gl_entries(gl_entries) + self.add_party_gl_entries(gl_entries, is_reconcile) self.add_bank_gl_entries(gl_entries) self.add_deductions_gl_entries(gl_entries) self.add_tax_gl_entries(gl_entries) @@ -888,7 +888,7 @@ class PaymentEntry(AccountsController): gl_entries = process_gl_map(gl_entries) make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj) - def add_party_gl_entries(self, gl_entries): + def add_party_gl_entries(self, gl_entries, is_reconcile): if self.party_account: if self.payment_type == "Receive": against_account = self.paid_to @@ -917,13 +917,14 @@ class PaymentEntry(AccountsController): d.reference_doctype in ["Sales Invoice", "Purchase Invoice"] and book_advance_payments_as_liability ): - self.make_invoice_liability_entry(gl_entries, d) - gle.update( - { - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, - } - ) + if not is_reconcile: + self.make_invoice_liability_entry(gl_entries, d) + gle.update( + { + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, + } + ) allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) gle.update( diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 1ac231bc19f..6ebb6afe6e7 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1664,13 +1664,9 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): def test_advance_entries_as_liability(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry - from erpnext.accounts.party import get_party_account - frappe.db.set_value( - "Company", - "_Test Company", - {"book_advance_payments_as_liability": 1, "default_advance_paid_account": "Debtors - _TC"}, - ) + set_advance_flag(company="_Test Company", flag=1, default_account="Debtors - _TC") + pe = create_payment_entry( company="_Test Company", payment_type="Pay", @@ -1678,34 +1674,48 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): party="_Test Supplier", paid_from="Cash - _TC", paid_to="Creditors - _TC", - paid_amount=1000, + paid_amount=500, ) pe.submit() pi = make_purchase_invoice( - company="_Test Company", customer="_Test Supplier", do_not_save=True, do_not_submit=True + company="_Test Company", + customer="_Test Supplier", + do_not_save=True, + do_not_submit=True, + rate=1000, + price_list_rate=1000, + qty=1, ) - pi.base_grand_total = 100 - pi.grand_total = 100 + pi.base_grand_total = 1000 + pi.grand_total = 1000 pi.set_advances() - self.assertEqual(pi.advances[0].allocated_amount, 100) - pi.advances[0].allocated_amount = 50 - pi.advances = [pi.advances[0]] + for advance in pi.advances: + advance.allocated_amount = 500 if advance.reference_name == pe.name else 0 pi.save() pi.submit() - expected_gle = [ - ["Creditors - _TC", 50, 100, nowdate()], - ["Debtors - _TC", 0.0, 50, nowdate()], - ["Stock Received But Not Billed - _TC", 100, 0.0, nowdate()], - ] + self.assertEqual(pi.advances[0].allocated_amount, 500) + expected_gle = [ + ["Creditors - _TC", 500, 1000, nowdate()], + ["Debtors - _TC", 0.0, 500, nowdate()], + ["Stock Received But Not Billed - _TC", 1000, 0.0, nowdate()], + ] check_gl_entries(self, pi.name, expected_gle, nowdate()) - self.assertEqual(pi.outstanding_amount, 200) - frappe.db.set_value( - "Company", - "_Test Company", - {"book_advance_payments_as_liability": 0, "default_advance_paid_account": ""}, - ) + self.assertEqual(pi.outstanding_amount, 500) + + set_advance_flag(company="_Test Company", flag=0, default_account="") + + +def set_advance_flag(company, flag, default_account): + frappe.db.set_value( + "Company", + company, + { + "book_advance_payments_as_liability": flag, + "default_advance_paid_account": default_account, + }, + ) def check_gl_entries(doc, voucher_no, expected_gle, posting_date): diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 8ab7fd7cd0c..067808d9a1b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3315,16 +3315,10 @@ class TestSalesInvoice(unittest.TestCase): def test_advance_entries_as_liability(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry - from erpnext.accounts.party import get_party_account + from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute + + set_advance_flag(company="_Test Company", flag=1, default_account="Creditors - _TC") - frappe.db.set_value( - "Company", - "_Test Company", - { - "book_advance_payments_as_liability": 1, - "default_advance_received_account": "Creditors - _TC", - }, - ) pe = create_payment_entry( company="_Test Company", payment_type="Receive", @@ -3337,32 +3331,42 @@ class TestSalesInvoice(unittest.TestCase): pe.submit() si = create_sales_invoice( - company="_Test Company", customer="_Test Customer", do_not_save=True, do_not_submit=True + company="_Test Company", + customer="_Test Customer", + do_not_save=True, + do_not_submit=True, + rate=500, + price_list_rate=500, ) - si.base_grand_total = 100 - si.grand_total = 100 + si.base_grand_total = 500 + si.grand_total = 500 si.set_advances() - self.assertEqual(si.advances[0].allocated_amount, 100) - si.advances[0].allocated_amount = 50 - si.advances = [si.advances[0]] + for advance in si.advances: + advance.allocated_amount = 500 if advance.reference_name == pe.name else 0 si.save() si.submit() - expected_gle = [ - ["Creditors - _TC", 50, 0.0, nowdate()], - ["Debtors - _TC", 100, 50, nowdate()], - ["Sales - _TC", 0.0, 100, nowdate()], - ] + self.assertEqual(si.advances[0].allocated_amount, 500) + expected_gle = [ + ["Creditors - _TC", 500, 0.0, nowdate()], + ["Debtors - _TC", 500, 500, nowdate()], + ["Sales - _TC", 0.0, 500, nowdate()], + ] check_gl_entries(self, si.name, expected_gle, nowdate()) - self.assertEqual(si.outstanding_amount, 50) - frappe.db.set_value( - "Company", - "_Test Company", - { - "book_advance_payments_as_liability": 0, - "default_advance_received_account": "", - }, - ) + self.assertEqual(si.outstanding_amount, 0) + + set_advance_flag(company="_Test Company", flag=0, default_account="") + + +def set_advance_flag(company, flag, default_account): + frappe.db.set_value( + "Company", + company, + { + "book_advance_payments_as_liability": flag, + "default_advance_received_account": default_account, + }, + ) def get_sales_invoice_for_e_invoice(): diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index c1d365304fc..728a6d7a968 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -472,7 +472,7 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n doc.save(ignore_permissions=True) # re-submit advance entry doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) - gl_map = doc.build_gl_map() + gl_map = doc.build_gl_map(is_reconcile=True) create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) # Only update outstanding for newly linked vouchers diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 50f9bb6cb9d..a5bf4b246bb 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -22,7 +22,7 @@ frappe.ui.form.on("Supplier", { let d = locals[cdt][cdn]; return { filters: { - "root_type": 'Asset', + "account_type": "Receivable", "company": d.company, "is_group": 0 } diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3fac56cfa2b..7b2039d1934 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2940,10 +2940,8 @@ def make_advance_liability_entry( dr_or_cr + "_in_account_currency": allocated_amount, rev: 0, rev + "_in_account_currency": 0, - "against_voucher": invoice.return_against - if cint(invoice.is_return) and invoice.return_against - else invoice.name, - "against_voucher_type": invoice.doctype, + "against_voucher": pe.name, + "against_voucher_type": "Payment Entry", "cost_center": invoice.cost_center, "project": invoice.project, "voucher_type": voucher_type, diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 8f828807a59..408e89b0ef3 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -39,7 +39,7 @@ frappe.ui.form.on("Customer", { let d = locals[cdt][cdn]; return { filters: { - "root_type": 'Liability', + "account_type": 'Payable', "company": d.company, "is_group": 0 } diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 436fe0595a6..089e20d4423 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -227,8 +227,8 @@ erpnext.company.setup_queries = function(frm) { ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}], ["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}], ["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}], - ["default_advance_received_account", {"root_type": "Liability"}], - ["default_advance_paid_account", {"root_type": "Asset"}], + ["default_advance_received_account", {"account_type": "Payable"}], + ["default_advance_paid_account", {"account_type": "Receivable"}], ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); From ba4ab06ae3024d387570ef6779ab218f72518ed1 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 14 Jun 2023 12:39:16 +0530 Subject: [PATCH 08/29] fix: changed account types in controller method --- .../doctype/payment_entry/payment_entry.py | 34 +++++++++++++------ erpnext/accounts/utils.py | 6 ++-- erpnext/controllers/accounts_controller.py | 2 +- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index dd3490936ff..7c813eb3293 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -99,11 +99,11 @@ class PaymentEntry(AccountsController): ) if not book_advance_payments_as_liability: return - root_type = frappe.get_value( - "Account", {"name": self.party_account, "company": self.company}, "root_type" + account_type = frappe.get_value( + "Account", {"name": self.party_account, "company": self.company}, "account_type" ) - if (root_type == "Liability" and self.party_type == "Customer") or ( - root_type == "Asset" and self.party_type == "Supplier" + if (account_type == "Payable" and self.party_type == "Customer") or ( + account_type == "Receivable" and self.party_type == "Supplier" ): return if self.unallocated_amount == 0: @@ -908,6 +908,8 @@ class PaymentEntry(AccountsController): }, item=self, ) + is_advance = self.get_advance_flag() + for d in self.get("references"): gle = party_dict.copy() book_advance_payments_as_liability = frappe.get_value( @@ -916,13 +918,14 @@ class PaymentEntry(AccountsController): if ( d.reference_doctype in ["Sales Invoice", "Purchase Invoice"] and book_advance_payments_as_liability + and is_advance ): if not is_reconcile: self.make_invoice_liability_entry(gl_entries, d) gle.update( { - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, + "voucher_type": "Payment Entry", + "voucher_no": self.name, } ) @@ -940,7 +943,6 @@ class PaymentEntry(AccountsController): "against_voucher": d.reference_name, } ) - gl_entries.append(gle) if self.unallocated_amount: @@ -952,13 +954,19 @@ class PaymentEntry(AccountsController): { dr_or_cr + "_in_account_currency": self.unallocated_amount, dr_or_cr: base_unallocated_amount, - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, } ) gl_entries.append(gle) + def get_advance_flag(self): + for d in self.get("references"): + if d.reference_doctype == "Sales Order": + return True + if self.unallocated_amount > 0: + return True + return False + def make_invoice_liability_entry(self, gl_entries, invoice): args_dict = { "party_type": self.party_type, @@ -967,8 +975,6 @@ class PaymentEntry(AccountsController): "cost_center": self.cost_center, "voucher_type": invoice.reference_doctype, "voucher_no": invoice.reference_name, - "against_voucher_type": invoice.reference_doctype, - "against_voucher": invoice.reference_name, } dr_or_cr = "credit" if invoice.reference_doctype == "Sales Invoice" else "debit" @@ -987,6 +993,12 @@ class PaymentEntry(AccountsController): args_dict["account"] = self.party_account args_dict[dr_or_cr] = invoice.allocated_amount args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount + args_dict.update( + { + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, + } + ) gle = self.get_gl_dict( args_dict, item=self, diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 728a6d7a968..abf9b5e9b2a 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -436,7 +436,9 @@ def add_cc(args=None): return cc.name -def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # nosemgrep +def reconcile_against_document( + args, skip_ref_details_update_for_pe=False, is_reconcile=False +): # nosemgrep """ Cancel PE or JV, Update against document, split if required and resubmit """ @@ -472,7 +474,7 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n doc.save(ignore_permissions=True) # re-submit advance entry doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) - gl_map = doc.build_gl_map(is_reconcile=True) + gl_map = doc.build_gl_map(is_reconcile) create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) # Only update outstanding for newly linked vouchers diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 7b2039d1934..578b26a2cde 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1078,7 +1078,7 @@ class AccountsController(TransactionBase): if lst: from erpnext.accounts.utils import reconcile_against_document - reconcile_against_document(lst) + reconcile_against_document(lst, is_reconcile=True) def on_cancel(self): from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries From 033e4e84f5c91ef1e6a36ffb9ff66cd41a1fd108 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 14 Jun 2023 14:20:42 +0530 Subject: [PATCH 09/29] fix: modify voucher details for liability entries --- .../doctype/payment_entry/payment_entry.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7c813eb3293..40dcc8cc680 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -924,8 +924,8 @@ class PaymentEntry(AccountsController): self.make_invoice_liability_entry(gl_entries, d) gle.update( { - "voucher_type": "Payment Entry", - "voucher_no": self.name, + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, } ) @@ -973,14 +973,20 @@ class PaymentEntry(AccountsController): "party": self.party, "account_currency": self.party_account_currency, "cost_center": self.cost_center, - "voucher_type": invoice.reference_doctype, - "voucher_no": invoice.reference_name, + "voucher_type": "Payment Entry", + "voucher_no": self.name, } dr_or_cr = "credit" if invoice.reference_doctype == "Sales Invoice" else "debit" args_dict["account"] = invoice.account args_dict[dr_or_cr] = invoice.allocated_amount args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount + args_dict.update( + { + "against_voucher_type": invoice.reference_doctype, + "against_voucher": invoice.reference_name, + } + ) gle = self.get_gl_dict( args_dict, item=self, From 442e3f2aa271454ff6b6001bc773df86196b0880 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 16 Jun 2023 13:38:47 +0530 Subject: [PATCH 10/29] fix: update outstanding amount and unpaid status on cancellation of payment entry --- .../doctype/payment_entry/payment_entry.py | 25 ++++++++-------- erpnext/accounts/general_ledger.py | 1 + erpnext/accounts/utils.py | 2 +- erpnext/controllers/accounts_controller.py | 29 +++++++------------ erpnext/setup/doctype/company/company.json | 15 +++++----- 5 files changed, 33 insertions(+), 39 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 40dcc8cc680..d901faaed3e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -872,7 +872,7 @@ class PaymentEntry(AccountsController): self.set("remarks", "\n".join(remarks)) - def build_gl_map(self, is_reconcile=False): + def build_gl_map(self, is_reconcile=True): if self.payment_type in ("Receive", "Pay") and not self.get("party_account_field"): self.setup_party_account_field() @@ -918,16 +918,15 @@ class PaymentEntry(AccountsController): if ( d.reference_doctype in ["Sales Invoice", "Purchase Invoice"] and book_advance_payments_as_liability - and is_advance + and (is_advance or is_reconcile) ): - if not is_reconcile: - self.make_invoice_liability_entry(gl_entries, d) - gle.update( - { - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, - } - ) + self.make_invoice_liability_entry(gl_entries, d) + gle.update( + { + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, + } + ) allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) gle.update( @@ -939,8 +938,8 @@ class PaymentEntry(AccountsController): if not gle.get("against_voucher_type"): gle.update( { - "against_voucher_type": d.reference_doctype, - "against_voucher": d.reference_name, + "against_voucher_type": d.reference_doctype if is_advance else "Payment Entry", + "against_voucher": d.reference_name if is_advance else self.name, } ) gl_entries.append(gle) @@ -954,6 +953,8 @@ class PaymentEntry(AccountsController): { dr_or_cr + "_in_account_currency": self.unallocated_amount, dr_or_cr: base_unallocated_amount, + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, } ) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index a929ff17b09..a0954a955a0 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -223,6 +223,7 @@ def check_if_in_list(gle, gl_map, dimensions=None): "party_type", "project", "finance_book", + "voucher_no", ] if dimensions: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index abf9b5e9b2a..e69dcd4199e 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -437,7 +437,7 @@ def add_cc(args=None): def reconcile_against_document( - args, skip_ref_details_update_for_pe=False, is_reconcile=False + args, skip_ref_details_update_for_pe=False, is_reconcile=True ): # nosemgrep """ Cancel PE or JV, Update against document, split if required and resubmit diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 578b26a2cde..f564840251b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1021,7 +1021,7 @@ class AccountsController(TransactionBase): ) ) - def update_against_document_in_jv(self): + def update_against_document_in_jv(self, is_reconcile=True): """ Links invoice and advance voucher: 1. cancel advance voucher @@ -1078,7 +1078,7 @@ class AccountsController(TransactionBase): if lst: from erpnext.accounts.utils import reconcile_against_document - reconcile_against_document(lst, is_reconcile=True) + reconcile_against_document(lst, is_reconcile) def on_cancel(self): from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries @@ -2919,7 +2919,6 @@ def make_advance_liability_entry( rev = "credit" against = invoice.debit_to party = invoice.customer - voucher_type = "Sales Invoice" else: invoice = frappe.get_doc("Purchase Invoice", invoice) account = pe.paid_to @@ -2927,9 +2926,8 @@ def make_advance_liability_entry( rev = "debit" against = invoice.credit_to party = invoice.supplier - voucher_type = "Purchase Invoice" gl_entries.append( - invoice.get_gl_dict( + pe.get_gl_dict( { "account": account, "party_type": party_type, @@ -2940,42 +2938,35 @@ def make_advance_liability_entry( dr_or_cr + "_in_account_currency": allocated_amount, rev: 0, rev + "_in_account_currency": 0, - "against_voucher": pe.name, - "against_voucher_type": "Payment Entry", "cost_center": invoice.cost_center, "project": invoice.project, - "voucher_type": voucher_type, - "voucher_no": invoice.name, + "against_voucher_type": "Payment Entry", + "against_voucher": pe.name, }, invoice.party_account_currency, - item=invoice, + item=pe, ) ) (dr_or_cr, rev) = ("credit", "debit") if party_type == "Customer" else ("debit", "credit") gl_entries.append( - invoice.get_gl_dict( + pe.get_gl_dict( { "account": against, "party_type": party_type, "party": party, "due_date": invoice.due_date, - "against": account, dr_or_cr: allocated_amount, dr_or_cr + "_in_account_currency": allocated_amount, rev: 0, rev + "_in_account_currency": 0, - "against_voucher": invoice.return_against - if cint(invoice.is_return) and invoice.return_against - else invoice.name, - "against_voucher_type": invoice.doctype, "cost_center": invoice.cost_center, "project": invoice.project, - "voucher_type": "Payment Entry" if references else voucher_type, - "voucher_no": pe.name if references else invoice.name, + "against_voucher_type": invoice.doctype, + "against_voucher": invoice.name, }, invoice.party_account_currency, - item=invoice, + item=pe, ) ) diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 3523af1988f..611e2ab2210 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -72,9 +72,9 @@ "default_finance_book", "advance_payments_section", "book_advance_payments_as_liability", + "column_break_fwcf", "default_advance_received_account", "default_advance_paid_account", - "column_break_cui0", "auto_accounting_for_stock_settings", "enable_perpetual_inventory", "enable_provisional_accounting_for_non_stock_items", @@ -702,19 +702,16 @@ }, { "default": "0", + "description": "Enabling this option will allow you to record -

1. Advances Received in a Liability Account instead of the Receivable Account

2. Advances Paid in an Asset Account instead of the Payable Account", "fieldname": "book_advance_payments_as_liability", "fieldtype": "Check", - "label": "Book Advance Payments as Liability" + "label": "Book Advance Payments in Separate Party Account" }, { "fieldname": "advance_payments_section", "fieldtype": "Section Break", "label": "Advance Payments" }, - { - "fieldname": "column_break_cui0", - "fieldtype": "Column Break" - }, { "depends_on": "eval:doc.book_advance_payments_as_liability", "fieldname": "default_advance_received_account", @@ -730,6 +727,10 @@ "label": "Default Advance Paid Account", "mandatory_depends_on": "book_advance_payments_as_liability", "options": "Account" + }, + { + "fieldname": "column_break_fwcf", + "fieldtype": "Column Break" } ], "icon": "fa fa-building", @@ -737,7 +738,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-06-12 12:51:12.007410", + "modified": "2023-06-16 13:32:48.790947", "modified_by": "Administrator", "module": "Setup", "name": "Company", From 016ed951da02ca87e735f035f5c6663cc113ad76 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 20 Jun 2023 13:22:32 +0530 Subject: [PATCH 11/29] test: Update tests --- .../doctype/payment_entry/payment_entry.py | 42 ++++++++++--------- .../purchase_invoice/test_purchase_invoice.py | 16 ++++--- .../sales_invoice/test_sales_invoice.py | 17 ++++---- erpnext/accounts/utils.py | 6 +-- erpnext/controllers/accounts_controller.py | 4 +- 5 files changed, 46 insertions(+), 39 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index d23afb17d94..752d22a3532 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -97,29 +97,37 @@ class PaymentEntry(AccountsController): book_advance_payments_as_liability = frappe.get_value( "Company", {"company_name": self.company}, "book_advance_payments_as_liability" ) + if not book_advance_payments_as_liability: return + account_type = frappe.get_value( "Account", {"name": self.party_account, "company": self.company}, "account_type" ) + if (account_type == "Payable" and self.party_type == "Customer") or ( account_type == "Receivable" and self.party_type == "Supplier" ): return + if self.unallocated_amount == 0: for d in self.references: if d.reference_doctype in ["Sales Order", "Purchase Order"]: break else: return + liability_account = get_party_account( self.party_type, self.party, self.company, include_advance=True )[1] + self.set(self.party_account_field, liability_account) + msg = "Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.".format( frappe.bold(self.party_account), frappe.bold(liability_account), ) + frappe.msgprint(_(msg), alert=True) def on_cancel(self): @@ -921,12 +929,12 @@ class PaymentEntry(AccountsController): self.set("remarks", "\n".join(remarks)) - def build_gl_map(self, is_reconcile=True): + def build_gl_map(self): if self.payment_type in ("Receive", "Pay") and not self.get("party_account_field"): self.setup_party_account_field() gl_entries = [] - self.add_party_gl_entries(gl_entries, is_reconcile) + self.add_party_gl_entries(gl_entries) self.add_bank_gl_entries(gl_entries) self.add_deductions_gl_entries(gl_entries) self.add_tax_gl_entries(gl_entries) @@ -937,7 +945,7 @@ class PaymentEntry(AccountsController): gl_entries = process_gl_map(gl_entries) make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj) - def add_party_gl_entries(self, gl_entries, is_reconcile): + def add_party_gl_entries(self, gl_entries): if self.party_account: if self.payment_type == "Receive": against_account = self.paid_to @@ -957,7 +965,7 @@ class PaymentEntry(AccountsController): }, item=self, ) - is_advance = self.get_advance_flag() + is_advance = self.is_advance_entry() for d in self.get("references"): gle = party_dict.copy() @@ -967,30 +975,24 @@ class PaymentEntry(AccountsController): if ( d.reference_doctype in ["Sales Invoice", "Purchase Invoice"] and book_advance_payments_as_liability - and (is_advance or is_reconcile) + and is_advance ): self.make_invoice_liability_entry(gl_entries, d) - gle.update( - { - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, - } - ) + against_voucher_type = "Payment Entry" + against_voucher = self.name + else: + against_voucher_type = d.reference_doctype + against_voucher = d.reference_name allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) gle.update( { dr_or_cr: allocated_amount_in_company_currency, dr_or_cr + "_in_account_currency": d.allocated_amount, + "against_voucher_type": against_voucher_type, + "against_voucher": against_voucher, } ) - if not gle.get("against_voucher_type"): - gle.update( - { - "against_voucher_type": d.reference_doctype if is_advance else "Payment Entry", - "against_voucher": d.reference_name if is_advance else self.name, - } - ) gl_entries.append(gle) if self.unallocated_amount: @@ -1009,9 +1011,9 @@ class PaymentEntry(AccountsController): gl_entries.append(gle) - def get_advance_flag(self): + def is_advance_entry(self): for d in self.get("references"): - if d.reference_doctype == "Sales Order": + if d.reference_doctype in ("Sales Order", "Purchase Order"): return True if self.unallocated_amount > 0: return True diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 02d60dccab4..569a48acad6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1698,12 +1698,16 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): pi.submit() self.assertEqual(pi.advances[0].allocated_amount, 500) + + # Check GL Entry against payment doctype expected_gle = [ - ["Creditors - _TC", 500, 1000, nowdate()], + ["Cash - _TC", 0.0, 500, nowdate()], + ["Creditors - _TC", 500, 0.0, nowdate()], + ["Creditors - _TC", 500, 0.0, nowdate()], ["Debtors - _TC", 0.0, 500, nowdate()], - ["Stock Received But Not Billed - _TC", 1000, 0.0, nowdate()], ] - check_gl_entries(self, pi.name, expected_gle, nowdate()) + + check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry") self.assertEqual(pi.outstanding_amount, 500) set_advance_flag(company="_Test Company", flag=0, default_account="") @@ -1735,18 +1739,18 @@ def set_advance_flag(company, flag, default_account): ) -def check_gl_entries(doc, voucher_no, expected_gle, posting_date): +def check_gl_entries(doc, voucher_no, expected_gle, posting_date, voucher_type="Purchase Invoice"): gl = frappe.qb.DocType("GL Entry") q = ( frappe.qb.from_(gl) .select(gl.account, gl.debit, gl.credit, gl.posting_date) .where( - (gl.voucher_type == "Sales Invoice") + (gl.voucher_type == voucher_type) & (gl.voucher_no == voucher_no) & (gl.posting_date >= posting_date) & (gl.is_cancelled == 0) ) - .orderby(gl.posting_date, gl.account) + .orderby(gl.posting_date, gl.account, gl.creation) ) gl_entries = q.run(as_dict=True) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index c23ef34759c..4cf3abaad13 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3312,7 +3312,6 @@ class TestSalesInvoice(unittest.TestCase): def test_advance_entries_as_liability(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry - from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute set_advance_flag(company="_Test Company", flag=1, default_account="Creditors - _TC") @@ -3344,12 +3343,16 @@ class TestSalesInvoice(unittest.TestCase): si.submit() self.assertEqual(si.advances[0].allocated_amount, 500) + + # Check GL Entry against payment doctype expected_gle = [ + ["Cash - _TC", 1000, 0.0, nowdate()], ["Creditors - _TC", 500, 0.0, nowdate()], - ["Debtors - _TC", 500, 500, nowdate()], - ["Sales - _TC", 0.0, 500, nowdate()], + ["Debtors - _TC", 0.0, 1000, nowdate()], + ["Debtors - _TC", 0.0, 500, nowdate()], ] - check_gl_entries(self, si.name, expected_gle, nowdate()) + + check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry") self.assertEqual(si.outstanding_amount, 0) set_advance_flag(company="_Test Company", flag=0, default_account="") @@ -3401,18 +3404,18 @@ def get_sales_invoice_for_e_invoice(): return si -def check_gl_entries(doc, voucher_no, expected_gle, posting_date): +def check_gl_entries(doc, voucher_no, expected_gle, posting_date, voucher_type="Sales Invoice"): gl = frappe.qb.DocType("GL Entry") q = ( frappe.qb.from_(gl) .select(gl.account, gl.debit, gl.credit, gl.posting_date) .where( - (gl.voucher_type == "Sales Invoice") + (gl.voucher_type == voucher_type) & (gl.voucher_no == voucher_no) & (gl.posting_date >= posting_date) & (gl.is_cancelled == 0) ) - .orderby(gl.posting_date, gl.account) + .orderby(gl.posting_date, gl.account, gl.creation) ) gl_entries = q.run(as_dict=True) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index e69dcd4199e..c1d365304fc 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -436,9 +436,7 @@ def add_cc(args=None): return cc.name -def reconcile_against_document( - args, skip_ref_details_update_for_pe=False, is_reconcile=True -): # nosemgrep +def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # nosemgrep """ Cancel PE or JV, Update against document, split if required and resubmit """ @@ -474,7 +472,7 @@ def reconcile_against_document( doc.save(ignore_permissions=True) # re-submit advance entry doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) - gl_map = doc.build_gl_map(is_reconcile) + gl_map = doc.build_gl_map() create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) # Only update outstanding for newly linked vouchers diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index dc499b91559..80f84307245 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1025,7 +1025,7 @@ class AccountsController(TransactionBase): ) ) - def update_against_document_in_jv(self, is_reconcile=True): + def update_against_document_in_jv(self): """ Links invoice and advance voucher: 1. cancel advance voucher @@ -1082,7 +1082,7 @@ class AccountsController(TransactionBase): if lst: from erpnext.accounts.utils import reconcile_against_document - reconcile_against_document(lst, is_reconcile) + reconcile_against_document(lst) def on_cancel(self): from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries From 92f845c0e1093ec1eeeb6e3bcd66d27ca4f5ca29 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jun 2023 12:21:19 +0530 Subject: [PATCH 12/29] chore: Advance fetching order --- erpnext/accounts/party.py | 6 +- erpnext/controllers/accounts_controller.py | 164 +++++++++++---------- 2 files changed, 91 insertions(+), 79 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index d6aa7d84027..03cf82a2b04 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -410,7 +410,11 @@ def get_party_account(party_type, party=None, company=None, include_advance=Fals if include_advance and party_type in ["Customer", "Supplier"]: advance_account = get_party_advance_account(party_type, party, company) - return [account, advance_account] + if advance_account: + return [account, advance_account] + else: + return [account] + return account diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 80f84307245..eec58e853d2 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -892,9 +892,10 @@ class AccountsController(TransactionBase): amount_field = "debit_in_account_currency" order_field = "purchase_order" order_doctype = "Purchase Order" - party_account = [ - get_party_account(party_type, party=party, company=self.company, include_advance=True)[1] - ] + + party_account = get_party_account( + party_type, party=party, company=self.company, include_advance=True + ) order_list = list(set(d.get(order_field) for d in self.get("items") if d.get(order_field))) @@ -2203,37 +2204,59 @@ def get_advance_payment_entries( condition=None, ): - q = build_query( - party_type, - party, - party_account, - order_doctype, - order_list, - include_unallocated, - against_all_orders, - limit, - condition, - ) + payment_entries = [] + payment_entry = frappe.qb.DocType("Payment Entry") - payment_entries = q.run(as_dict=True) + if order_list or against_all_orders: + q = get_common_query( + party_type, + party, + party_account, + limit, + condition, + ) + payment_ref = frappe.qb.DocType("Payment Entry Reference") - return list(payment_entries) + q = q.inner_join(payment_ref).on(payment_entry.name == payment_ref.parent) + q = q.select( + (payment_ref.allocated_amount).as_("amount"), + (payment_ref.name).as_("reference_row"), + (payment_ref.reference_name).as_("against_order"), + payment_ref.reference_doctype == order_doctype, + ) + + if order_list: + q = q.where(payment_ref.reference_name.isin(order_list)) + + allocated = list(q.run(as_dict=True)) + payment_entries += allocated + + if include_unallocated: + q = get_common_query( + party_type, + party, + party_account, + limit, + condition, + ) + q = q.select((payment_entry.unallocated_amount).as_("amount")) + q = q.where(payment_entry.unallocated_amount > 0) + + unallocated = list(q.run(as_dict=True)) + payment_entries += unallocated + + return payment_entries -def build_query( +def get_common_query( party_type, party, party_account, - order_doctype, - order_list, - include_unallocated, - against_all_orders, limit, condition, ): payment_type = "Receive" if party_type == "Customer" else "Pay" payment_entry = frappe.qb.DocType("Payment Entry") - payment_ref = frappe.qb.DocType("Payment Entry Reference") q = ( frappe.qb.from_(payment_entry) @@ -2259,68 +2282,53 @@ def build_query( q = q.where(payment_entry.paid_to.isin(party_account)) if payment_type == "Receive": - q = q.select(payment_entry.source_exchange_rate) + q = q.select((payment_entry.source_exchange_rate).as_("exchange_rate")) else: - q.select(payment_entry.target_exchange_rate) + q = q.select((payment_entry.target_exchange_rate).as_("exchange_rate")) - if include_unallocated: - q = q.select((payment_entry.unallocated_amount).as_("amount")) - q = q.where(payment_entry.unallocated_amount > 0) - - if condition: - q = q.where(payment_entry.company == condition["company"]) - q = ( - q.where(payment_entry.posting_date >= condition["from_payment_date"]) - if condition.get("from_payment_date") - else q - ) - q = ( - q.where(payment_entry.posting_date <= condition["to_payment_date"]) - if condition.get("to_payment_date") - else q - ) - if condition.get("get_payments") == True: - q = ( - q.where(payment_entry.cost_center == condition["cost_center"]) - if condition.get("cost_center") - else q - ) - q = ( - q.where(payment_entry.unallocated_amount >= condition["minimum_payment_amount"]) - if condition.get("minimum_payment_amount") - else q - ) - q = ( - q.where(payment_entry.unallocated_amount <= condition["maximum_payment_amount"]) - if condition.get("maximum_payment_amount") - else q - ) - else: - q = ( - q.where(payment_entry.total_debit >= condition["minimum_payment_amount"]) - if condition.get("minimum_payment_amount") - else q - ) - q = ( - q.where(payment_entry.total_debit <= condition["maximum_payment_amount"]) - if condition.get("maximum_payment_amount") - else q - ) - - elif order_list or against_all_orders: - q = q.inner_join(payment_ref).on(payment_entry.name == payment_ref.parent) - q = q.select( - (payment_ref.allocated_amount).as_("amount"), - (payment_ref.name).as_("reference_row"), - (payment_ref.reference_name).as_("against_order"), - payment_ref.reference_doctype == order_doctype, + if condition: + q = q.where(payment_entry.company == condition["company"]) + q = ( + q.where(payment_entry.posting_date >= condition["from_payment_date"]) + if condition.get("from_payment_date") + else q ) - - if order_list: - q = q.where(payment_ref.reference_name.isin(order_list)) + q = ( + q.where(payment_entry.posting_date <= condition["to_payment_date"]) + if condition.get("to_payment_date") + else q + ) + if condition.get("get_payments") == True: + q = ( + q.where(payment_entry.cost_center == condition["cost_center"]) + if condition.get("cost_center") + else q + ) + q = ( + q.where(payment_entry.unallocated_amount >= condition["minimum_payment_amount"]) + if condition.get("minimum_payment_amount") + else q + ) + q = ( + q.where(payment_entry.unallocated_amount <= condition["maximum_payment_amount"]) + if condition.get("maximum_payment_amount") + else q + ) + else: + q = ( + q.where(payment_entry.total_debit >= condition["minimum_payment_amount"]) + if condition.get("minimum_payment_amount") + else q + ) + q = ( + q.where(payment_entry.total_debit <= condition["maximum_payment_amount"]) + if condition.get("maximum_payment_amount") + else q + ) q = q.orderby(payment_entry.posting_date) q = q.limit(limit) if limit else q + return q From b64ebc6fcc4130dfeb8d83f44072a4dd01eca42a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jun 2023 17:49:45 +0530 Subject: [PATCH 13/29] test: fix payment reco tests --- .../test_process_deferred_accounting.py | 4 ++-- erpnext/controllers/accounts_controller.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py index 5a0aeb7284a..83646c90ba4 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py +++ b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py @@ -38,7 +38,7 @@ class TestProcessDeferredAccounting(unittest.TestCase): si.save() si.submit() - process_deferred_accounting = doc = frappe.get_doc( + process_deferred_accounting = frappe.get_doc( dict( doctype="Process Deferred Accounting", posting_date="2019-01-01", @@ -56,7 +56,7 @@ class TestProcessDeferredAccounting(unittest.TestCase): ["Sales - _TC", 0.0, 33.85, "2019-01-31"], ] - check_gl_entries(self, si.name, expected_gle, "2019-01-10") + check_gl_entries(self, si.name, expected_gle, "2019-01-31") def test_pda_submission_and_cancellation(self): pda = frappe.get_doc( diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index eec58e853d2..3dadd46ee84 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2273,11 +2273,11 @@ def get_common_query( ) if party_type == "Customer": - q = q.select(payment_entry.paid_from_account_currency) + q = q.select((payment_entry.paid_from_account_currency).as_("currency")) q = q.select(payment_entry.paid_from) q = q.where(payment_entry.paid_from.isin(party_account)) else: - q = q.select(payment_entry.paid_to_account_currency) + q = q.select((payment_entry.paid_to_account_currency).as_("currency")) q = q.select(payment_entry.paid_to) q = q.where(payment_entry.paid_to.isin(party_account)) From 3aead05f42ec62ee0edd2d1e9ca205e8ca6b0f2f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jun 2023 11:41:43 +0530 Subject: [PATCH 14/29] fix: Test related errors --- .../doctype/payment_entry/payment_entry.py | 7 +++++-- .../payment_reconciliation.py | 10 +++++++-- erpnext/accounts/utils.py | 2 +- erpnext/controllers/accounts_controller.py | 21 +++++++------------ 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 752d22a3532..f6c6bce5bc5 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -949,10 +949,8 @@ class PaymentEntry(AccountsController): if self.party_account: if self.payment_type == "Receive": against_account = self.paid_to - dr_or_cr = "credit" else: against_account = self.paid_from - dr_or_cr = "debit" party_dict = self.get_gl_dict( { @@ -965,6 +963,11 @@ class PaymentEntry(AccountsController): }, item=self, ) + + dr_or_cr = ( + "credit" if erpnext.get_party_account_type(self.party_type) == "Receivable" else "debit" + ) + is_advance = self.is_advance_entry() for d in self.get("references"): diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 9d869f21eec..e9cc3902d3b 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -59,7 +59,10 @@ class PaymentReconciliation(Document): self.add_payment_entries(non_reconciled_payments) def get_payment_entries(self): - party_account = [self.receivable_payable_account, self.default_advance_account] + if self.default_advance_account: + party_account = [self.receivable_payable_account, self.default_advance_account] + else: + party_account = [self.receivable_payable_account] order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" condition = frappe._dict( @@ -352,7 +355,10 @@ class PaymentReconciliation(Document): for row in self.get("allocation"): reconciled_entry = [] if row.invoice_number and row.allocated_amount: - if row.invoice_type in ["Sales Invoice", "Purchase Invoice"]: + if ( + row.invoice_type in ["Sales Invoice", "Purchase Invoice"] + and row.reference_type == "Payment Entry" + ): gl_entries = [] make_advance_liability_entry( gl_entries, row.reference_name, row.allocated_amount, row.invoice_number, self.party_type diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index c1d365304fc..d9561ad468e 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -500,7 +500,7 @@ def check_if_advance_entry_modified(args): q = ( frappe.qb.from_(journal_entry) - .innerjoin(journal_acc) + .inner_join(journal_acc) .on(journal_entry.name == journal_acc.parent) ) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3dadd46ee84..72c93261aa8 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2156,11 +2156,7 @@ def get_advance_journal_entries( ConstantColumn("Journal Entry").as_("reference_type"), (journal_entry.name).as_("reference_name"), (journal_entry.remark).as_("remarks"), - ( - journal_acc.debit_in_account_currency - if party_type == "Supplier" - else journal_acc.credit_in_account_currency - ).as_("amount"), + (journal_acc[amount_field]).as_("amount"), (journal_acc.name).as_("reference_row"), (journal_acc.reference_name).as_("against_order"), (journal_acc.exchange_rate), @@ -2179,12 +2175,13 @@ def get_advance_journal_entries( else: q = q.where(journal_acc.debit_in_account_currency > 0) + if include_unallocated: + q = q.where((journal_acc.reference_name.isnull()) | (journal_acc.reference_name == "")) + if order_list: - q = q.where(journal_acc.reference_type == order_doctype) - if include_unallocated: - q = q.where(journal_acc.reference_name.isin(order_list) | (journal_acc.reference_name == "")) - else: - q = q.where(journal_acc.reference_name.isin(order_list)) + q = q.where( + (journal_acc.reference_type == order_doctype) & ((journal_acc.reference).isin(order_list)) + ) q = q.orderby(journal_entry.posting_date) @@ -2222,15 +2219,14 @@ def get_advance_payment_entries( (payment_ref.allocated_amount).as_("amount"), (payment_ref.name).as_("reference_row"), (payment_ref.reference_name).as_("against_order"), - payment_ref.reference_doctype == order_doctype, ) + q = q.where(payment_ref.reference_doctype == order_doctype) if order_list: q = q.where(payment_ref.reference_name.isin(order_list)) allocated = list(q.run(as_dict=True)) payment_entries += allocated - if include_unallocated: q = get_common_query( party_type, @@ -2244,7 +2240,6 @@ def get_advance_payment_entries( unallocated = list(q.run(as_dict=True)) payment_entries += unallocated - return payment_entries From 6d121ae6e42b678a2879e6278e58e3441b2787d7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jun 2023 13:03:09 +0530 Subject: [PATCH 15/29] chore: fix typo --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 72c93261aa8..02fef4d31b1 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2180,7 +2180,7 @@ def get_advance_journal_entries( if order_list: q = q.where( - (journal_acc.reference_type == order_doctype) & ((journal_acc.reference).isin(order_list)) + (journal_acc.reference_type == order_doctype) & ((journal_acc.reference_type).isin(order_list)) ) q = q.orderby(journal_entry.posting_date) From d81d6069fb8688223fd5e777c781347990100aa7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jun 2023 18:28:16 +0530 Subject: [PATCH 16/29] fix: JV query --- erpnext/accounts/utils.py | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index d9561ad468e..f92e2c7cba7 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -502,24 +502,19 @@ def check_if_advance_entry_modified(args): frappe.qb.from_(journal_entry) .inner_join(journal_acc) .on(journal_entry.name == journal_acc.parent) - ) - - if args.get("dr_or_cr") == "debit_in_account_currency": - q = q.select(journal_acc.debit_in_account_currency) - else: - q = q.select(journal_acc.credit_in_account_currency) - - q = q.where( - (journal_acc.account == args.get("account")) - & ((journal_acc.party_type == args.get("party_type"))) - & ((journal_acc.party == args.get("party"))) - & ( - (journal_acc.reference_type == None) - | (journal_acc.reference_type.isin(["", "Sales Order", "Purchase Order"])) + .select(journal_acc[args.get("dr_or_cr")]) + .where( + (journal_acc.account == args.get("account")) + & ((journal_acc.party_type == args.get("party_type"))) + & ((journal_acc.party == args.get("party"))) + & ( + (journal_acc.reference_type.isnull()) + | (journal_acc.reference_type.isin(["", "Sales Order", "Purchase Order"])) + ) + & ((journal_entry.name == args.get("voucher_no"))) + & ((journal_acc.name == args.get("voucher_detail_no"))) + & ((journal_entry.docstatus == 1)) ) - & ((journal_entry.name == args.get("voucher_no"))) - & ((journal_acc.name == args.get("voucher_detail_no"))) - & ((journal_entry.docstatus == 1)) ) else: From b101dceb2a047731b8bd9931639d3ece6b66087e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jun 2023 19:38:33 +0530 Subject: [PATCH 17/29] test: GL Entry order --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 4cf3abaad13..6599e3960ad 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3252,9 +3252,9 @@ class TestSalesInvoice(unittest.TestCase): si.submit() expected_gle = [ - ["_Test Receivable USD - _TC", 7500.0, 500], ["Exchange Gain/Loss - _TC", 500.0, 0.0], ["Sales - _TC", 0.0, 7500.0], + ["_Test Receivable USD - _TC", 7500.0, 500], ] check_gl_entries(self, si.name, expected_gle, nowdate()) From 11a9bd523dd33430e18916e527abdbbc7a89d09f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jun 2023 20:26:12 +0530 Subject: [PATCH 18/29] test: Add posting date parameter --- .../accounts/doctype/sales_invoice/test_sales_invoice.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6599e3960ad..a5280bcbf46 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3252,9 +3252,10 @@ class TestSalesInvoice(unittest.TestCase): si.submit() expected_gle = [ - ["Exchange Gain/Loss - _TC", 500.0, 0.0], - ["Sales - _TC", 0.0, 7500.0], - ["_Test Receivable USD - _TC", 7500.0, 500], + ["_Test Receivable USD - _TC", 7500.0, 0.0, nowdate()], + ["_Test Receivable USD - _TC", 0.0, 500.0, nowdate()], + ["Exchange Gain/Loss - _TC", 500.0, 0.0, nowdate()], + ["Sales - _TC", 0.0, 7500.0, nowdate()], ] check_gl_entries(self, si.name, expected_gle, nowdate()) From 05c2198569f88785432c4ca6fee49fa04e268b03 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jun 2023 21:08:58 +0530 Subject: [PATCH 19/29] test: Update order --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index a5280bcbf46..e8e81fd82fe 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3252,9 +3252,9 @@ class TestSalesInvoice(unittest.TestCase): si.submit() expected_gle = [ + ["_Test Exchange Gain/Loss - _TC", 500.0, 0.0, nowdate()], ["_Test Receivable USD - _TC", 7500.0, 0.0, nowdate()], ["_Test Receivable USD - _TC", 0.0, 500.0, nowdate()], - ["Exchange Gain/Loss - _TC", 500.0, 0.0, nowdate()], ["Sales - _TC", 0.0, 7500.0, nowdate()], ] From da6bc1a13eb0effeb3f5e307d91ed6380e6dd823 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 23 Jun 2023 20:57:51 +0530 Subject: [PATCH 20/29] refactor: Redo workflows --- .../doctype/payment_entry/payment_entry.json | 12 +- .../doctype/payment_entry/payment_entry.py | 108 +++++++------ .../payment_reconciliation.py | 15 +- .../purchase_invoice/purchase_invoice.py | 14 +- .../purchase_invoice/test_purchase_invoice.py | 17 +- .../doctype/sales_invoice/sales_invoice.py | 16 +- .../sales_invoice/test_sales_invoice.py | 17 +- erpnext/accounts/general_ledger.py | 10 +- erpnext/accounts/utils.py | 4 +- erpnext/buying/doctype/supplier/supplier.js | 3 +- erpnext/controllers/accounts_controller.py | 147 +++++++++--------- erpnext/selling/doctype/customer/customer.js | 3 +- erpnext/setup/doctype/company/company.js | 4 +- erpnext/setup/doctype/company/company.json | 22 +-- .../doctype/customer_group/customer_group.js | 1 + .../doctype/supplier_group/supplier_group.js | 1 + 16 files changed, 208 insertions(+), 186 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 6224d4038d6..d7b6a198df8 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -19,6 +19,7 @@ "party_type", "party", "party_name", + "book_advance_payments_in_separate_party_account", "column_break_11", "bank_account", "party_bank_account", @@ -735,12 +736,21 @@ "fieldname": "get_outstanding_orders", "fieldtype": "Button", "label": "Get Outstanding Orders" + }, + { + "default": "0", + "fetch_from": "company.book_advance_payments_in_separate_party_account", + "fieldname": "book_advance_payments_in_separate_party_account", + "fieldtype": "Check", + "hidden": 1, + "label": "Book Advance Payments in Separate Party Account", + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-06-19 11:38:04.387219", + "modified": "2023-06-23 18:07:38.023010", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index f6c6bce5bc5..8141d0519e6 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -21,7 +21,11 @@ from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_ban from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import ( get_party_tax_withholding_details, ) -from erpnext.accounts.general_ledger import make_gl_entries, process_gl_map +from erpnext.accounts.general_ledger import ( + make_gl_entries, + make_reverse_gl_entries, + process_gl_map, +) from erpnext.accounts.party import get_party_account from erpnext.accounts.utils import get_account_currency, get_balance_on, get_outstanding_invoices from erpnext.controllers.accounts_controller import ( @@ -88,17 +92,14 @@ class PaymentEntry(AccountsController): if self.difference_amount: frappe.throw(_("Difference Amount must be zero")) self.make_gl_entries() + self.make_advance_gl_entries() self.update_outstanding_amounts() self.update_advance_paid() self.update_payment_schedule() self.set_status() def set_liability_account(self): - book_advance_payments_as_liability = frappe.get_value( - "Company", {"company_name": self.company}, "book_advance_payments_as_liability" - ) - - if not book_advance_payments_as_liability: + if not self.book_advance_payments_in_separate_party_account: return account_type = frappe.get_value( @@ -139,6 +140,7 @@ class PaymentEntry(AccountsController): "Repost Payment Ledger Items", ) self.make_gl_entries(cancel=1) + self.make_advance_gl_entries(cancel=1) self.update_outstanding_amounts() self.update_advance_paid() self.delink_advance_entry_references() @@ -212,7 +214,8 @@ class PaymentEntry(AccountsController): "party_account": self.paid_from if self.payment_type == "Receive" else self.paid_to, "get_outstanding_invoices": True, "get_orders_to_be_billed": True, - } + }, + validate=True, ) # Group latest_references by (voucher_type, voucher_no) @@ -417,6 +420,13 @@ class PaymentEntry(AccountsController): elif self.party_type == "Employee": ref_party_account = ref_doc.payable_account + if ref_party_account != self.party_account: + frappe.throw( + _("{0} {1} is associated with {2}, but Party Account is {3}").format( + d.reference_doctype, d.reference_name, ref_party_account, self.party_account + ) + ) + if ref_doc.doctype == "Purchase Invoice" and ref_doc.get("on_hold"): frappe.throw( _("{0} {1} is on hold").format(d.reference_doctype, d.reference_name), @@ -952,7 +962,7 @@ class PaymentEntry(AccountsController): else: against_account = self.paid_from - party_dict = self.get_gl_dict( + party_gl_dict = self.get_gl_dict( { "account": self.party_account, "party_type": self.party_type, @@ -968,32 +978,21 @@ class PaymentEntry(AccountsController): "credit" if erpnext.get_party_account_type(self.party_type) == "Receivable" else "debit" ) - is_advance = self.is_advance_entry() - for d in self.get("references"): - gle = party_dict.copy() - book_advance_payments_as_liability = frappe.get_value( - "Company", {"company_name": self.company}, "book_advance_payments_as_liability" - ) - if ( - d.reference_doctype in ["Sales Invoice", "Purchase Invoice"] - and book_advance_payments_as_liability - and is_advance - ): - self.make_invoice_liability_entry(gl_entries, d) - against_voucher_type = "Payment Entry" - against_voucher = self.name - else: - against_voucher_type = d.reference_doctype - against_voucher = d.reference_name + cost_center = self.cost_center + if d.reference_doctype == "Sales Invoice" and not cost_center: + cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center") + + gle = party_gl_dict.copy() allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) gle.update( { dr_or_cr: allocated_amount_in_company_currency, dr_or_cr + "_in_account_currency": d.allocated_amount, - "against_voucher_type": against_voucher_type, - "against_voucher": against_voucher, + "against_voucher_type": d.reference_doctype, + "against_voucher": d.reference_name, + "cost_center": cost_center, } ) gl_entries.append(gle) @@ -1002,25 +1001,44 @@ class PaymentEntry(AccountsController): exchange_rate = self.get_exchange_rate() base_unallocated_amount = self.unallocated_amount * exchange_rate - gle = party_dict.copy() + gle = party_gl_dict.copy() gle.update( { dr_or_cr + "_in_account_currency": self.unallocated_amount, dr_or_cr: base_unallocated_amount, - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, } ) gl_entries.append(gle) - def is_advance_entry(self): - for d in self.get("references"): - if d.reference_doctype in ("Sales Order", "Purchase Order"): - return True - if self.unallocated_amount > 0: - return True - return False + def make_advance_gl_entries(self, against_voucher_type=None, against_voucher=None, cancel=0): + if self.book_advance_payments_in_separate_party_account: + gl_entries = [] + for d in self.get("references"): + if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"): + 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) + + if cancel: + for entry in gl_entries: + frappe.db.set_value( + "GL Entry", + { + "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) + else: + make_gl_entries(gl_entries) def make_invoice_liability_entry(self, gl_entries, invoice): args_dict = { @@ -1030,6 +1048,7 @@ class PaymentEntry(AccountsController): "cost_center": self.cost_center, "voucher_type": "Payment Entry", "voucher_no": self.name, + "voucher_detail_no": invoice.name, } dr_or_cr = "credit" if invoice.reference_doctype == "Sales Invoice" else "debit" @@ -1391,7 +1410,7 @@ def validate_inclusive_tax(tax, doc): @frappe.whitelist() -def get_outstanding_reference_documents(args): +def get_outstanding_reference_documents(args, validate=False): if isinstance(args, str): args = json.loads(args) @@ -1511,13 +1530,14 @@ def get_outstanding_reference_documents(args): elif args.get("get_orders_to_be_billed"): ref_document_type = "orders" - frappe.msgprint( - _( - "No outstanding {0} found for the {1} {2} which qualify the filters you have specified." - ).format( - ref_document_type, _(args.get("party_type")).lower(), frappe.bold(args.get("party")) + if not validate: + frappe.msgprint( + _( + "No outstanding {0} found for the {1} {2} which qualify the filters you have specified." + ).format( + ref_document_type, _(args.get("party_type")).lower(), frappe.bold(args.get("party")) + ) ) - ) return data diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index e9cc3902d3b..8e2f0e5232b 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -12,16 +12,12 @@ import erpnext from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation import ( is_any_doc_running, ) -from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.utils import ( QueryPaymentLedger, get_outstanding_invoices, reconcile_against_document, ) -from erpnext.controllers.accounts_controller import ( - get_advance_payment_entries, - make_advance_liability_entry, -) +from erpnext.controllers.accounts_controller import get_advance_payment_entries class PaymentReconciliation(Document): @@ -355,15 +351,6 @@ class PaymentReconciliation(Document): for row in self.get("allocation"): reconciled_entry = [] if row.invoice_number and row.allocated_amount: - if ( - row.invoice_type in ["Sales Invoice", "Purchase Invoice"] - and row.reference_type == "Payment Entry" - ): - gl_entries = [] - make_advance_liability_entry( - gl_entries, row.reference_name, row.allocated_amount, row.invoice_number, self.party_type - ) - make_gl_entries(gl_entries) if row.reference_type in ["Sales Invoice", "Purchase Invoice"]: reconciled_entry = dr_or_cr_notes else: diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 68fa7bf1c90..230a8b3c586 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -33,10 +33,7 @@ from erpnext.accounts.utils import get_account_currency, get_fiscal_year from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.buying.utils import check_on_hold_or_closed_status -from erpnext.controllers.accounts_controller import ( - check_advance_liability_entry, - validate_account_head, -) +from erpnext.controllers.accounts_controller import validate_account_head from erpnext.controllers.buying_controller import BuyingController from erpnext.stock import get_warehouse_account_map from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( @@ -576,15 +573,6 @@ class PurchaseInvoice(BuyingController): gl_entries = [] self.make_supplier_gl_entry(gl_entries) - - check_advance_liability_entry( - gl_entries, - company=self.company, - advances=self.advances, - invoice=self.name, - party_type="Supplier", - ) - self.make_item_gl_entries(gl_entries) self.make_precision_loss_gl_entry(gl_entries) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 569a48acad6..8c964804786 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1664,10 +1664,17 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): self.assertTrue(return_pi.docstatus == 1) - def test_advance_entries_as_liability(self): + def test_advance_entries_as_asset(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry - set_advance_flag(company="_Test Company", flag=1, default_account="Debtors - _TC") + account = create_account( + parent_account="Current Assets - _TC", + account_name="Advances Paid", + company="_Test Company", + account_type="Receivable", + ) + + set_advance_flag(company="_Test Company", flag=1, default_account=account) pe = create_payment_entry( company="_Test Company", @@ -1701,13 +1708,15 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): # Check GL Entry against payment doctype expected_gle = [ + ["Advances Paid - _TC", 0.0, 500, nowdate()], ["Cash - _TC", 0.0, 500, nowdate()], ["Creditors - _TC", 500, 0.0, nowdate()], ["Creditors - _TC", 500, 0.0, nowdate()], - ["Debtors - _TC", 0.0, 500, nowdate()], ] check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry") + + pi.load_from_db() self.assertEqual(pi.outstanding_amount, 500) set_advance_flag(company="_Test Company", flag=0, default_account="") @@ -1733,7 +1742,7 @@ def set_advance_flag(company, flag, default_account): "Company", company, { - "book_advance_payments_as_liability": flag, + "book_advance_payments_in_separate_party_account": flag, "default_advance_paid_account": default_account, }, ) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index d2cd95fe9e0..25553cff0c6 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -32,10 +32,7 @@ from erpnext.assets.doctype.asset.depreciation import ( reset_depreciation_schedule, reverse_depreciation_entry_made_after_disposal, ) -from erpnext.controllers.accounts_controller import ( - check_advance_liability_entry, - validate_account_head, -) +from erpnext.controllers.accounts_controller import validate_account_head from erpnext.controllers.selling_controller import SellingController from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data from erpnext.setup.doctype.company.company import update_company_current_month_sales @@ -1055,21 +1052,12 @@ class SalesInvoice(SellingController): elif self.docstatus == 2 and cint(self.update_stock) and cint(auto_accounting_for_stock): make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) - def get_gl_entries(self, warehouse_account=None): + def get_gl_entries(self): from erpnext.accounts.general_ledger import merge_similar_entries gl_entries = [] self.make_customer_gl_entry(gl_entries) - - check_advance_liability_entry( - gl_entries, - company=self.company, - advances=self.advances, - invoice=self.name, - party_type="Customer", - ) - self.make_tax_gl_entries(gl_entries) self.make_exchange_gain_loss_gl_entries(gl_entries) self.make_internal_transfer_gl_entries(gl_entries) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index e8e81fd82fe..69ddfaac8df 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -6,7 +6,6 @@ import unittest import frappe from frappe.model.dynamic_links import get_dynamic_link_map -from frappe.model.naming import make_autoname from frappe.tests.utils import change_settings from frappe.utils import add_days, flt, getdate, nowdate, today @@ -35,7 +34,6 @@ from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle get_serial_nos_from_bundle, make_serial_batch_bundle, ) -from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError from erpnext.stock.doctype.stock_entry.test_stock_entry import ( get_qty_after_transaction, make_stock_entry, @@ -3314,7 +3312,14 @@ class TestSalesInvoice(unittest.TestCase): def test_advance_entries_as_liability(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry - set_advance_flag(company="_Test Company", flag=1, default_account="Creditors - _TC") + 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=account) pe = create_payment_entry( company="_Test Company", @@ -3347,13 +3352,15 @@ class TestSalesInvoice(unittest.TestCase): # Check GL Entry against payment doctype expected_gle = [ + ["Advances Received - _TC", 500, 0.0, nowdate()], ["Cash - _TC", 1000, 0.0, nowdate()], - ["Creditors - _TC", 500, 0.0, nowdate()], ["Debtors - _TC", 0.0, 1000, nowdate()], ["Debtors - _TC", 0.0, 500, nowdate()], ] check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry") + + si.load_from_db() self.assertEqual(si.outstanding_amount, 0) set_advance_flag(company="_Test Company", flag=0, default_account="") @@ -3364,7 +3371,7 @@ def set_advance_flag(company, flag, default_account): "Company", company, { - "book_advance_payments_as_liability": flag, + "book_advance_payments_in_separate_party_account": flag, "default_advance_received_account": default_account, }, ) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index a0954a955a0..22f39596e64 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -501,7 +501,12 @@ def get_round_off_account_and_cost_center( def make_reverse_gl_entries( - gl_entries=None, voucher_type=None, voucher_no=None, adv_adj=False, update_outstanding="Yes" + gl_entries=None, + voucher_type=None, + voucher_no=None, + adv_adj=False, + update_outstanding="Yes", + partial_cancel=False, ): """ Get original gl entries of the voucher @@ -528,7 +533,8 @@ def make_reverse_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"]) - set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"]) + if not partial_cancel: + set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"]) for entry in gl_entries: new_gle = copy.deepcopy(entry) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index f92e2c7cba7..5c256b730f3 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -474,6 +474,7 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) gl_map = doc.build_gl_map() create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) + doc.make_advance_gl_entries(entry.against_voucher_type, entry.against_voucher) # Only update outstanding for newly linked vouchers for entry in entries: @@ -731,8 +732,9 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): for pe in linked_pe: try: - pe_doc = frappe.get_doc("Payment Entry", pe) + pe_doc = frappe.get_doc("Payment Entry", pe, cache=True) pe_doc.set_amounts() + pe_doc.make_advance_gl_entries(against_voucher_type=ref_type, against_voucher=ref_no, cancel=1) pe_doc.clear_unallocated_reference_document_rows() pe_doc.validate_payment_type_with_outstanding() except Exception as e: diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index a5bf4b246bb..c9ac2797229 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -22,7 +22,8 @@ frappe.ui.form.on("Supplier", { let d = locals[cdt][cdn]; return { filters: { - "account_type": "Receivable", + "account_type": "Payable", + "root_type": "Asset", "company": d.company, "is_group": 0 } diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 02fef4d31b1..0d9ab6498db 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -756,6 +756,7 @@ class AccountsController(TransactionBase): "party": None, "project": self.get("project"), "post_net_value": args.get("post_net_value"), + "voucher_detail_no": args.get("voucher_detail_no"), } ) @@ -2915,82 +2916,82 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.create_stock_reservation_entries() -def make_advance_liability_entry( - gl_entries, pe, allocated_amount, invoice, party_type, references=False -): - pe = frappe.get_doc("Payment Entry", pe) - if party_type == "Customer": - invoice = frappe.get_doc("Sales Invoice", invoice) - account = pe.paid_from - dr_or_cr = "debit" - rev = "credit" - against = invoice.debit_to - party = invoice.customer - else: - invoice = frappe.get_doc("Purchase Invoice", invoice) - account = pe.paid_to - dr_or_cr = "credit" - rev = "debit" - against = invoice.credit_to - party = invoice.supplier - gl_entries.append( - pe.get_gl_dict( - { - "account": account, - "party_type": party_type, - "party": party, - "due_date": invoice.due_date, - "against": against, - dr_or_cr: allocated_amount, - dr_or_cr + "_in_account_currency": allocated_amount, - rev: 0, - rev + "_in_account_currency": 0, - "cost_center": invoice.cost_center, - "project": invoice.project, - "against_voucher_type": "Payment Entry", - "against_voucher": pe.name, - }, - invoice.party_account_currency, - item=pe, - ) - ) +# def make_advance_liability_entry( +# gl_entries, pe, allocated_amount, invoice, party_type +# ): +# pe = frappe.get_doc("Payment Entry", pe) +# if party_type == "Customer": +# invoice = frappe.get_doc("Sales Invoice", invoice) +# account = pe.paid_from +# dr_or_cr = "debit" +# rev = "credit" +# against = invoice.debit_to +# party = invoice.customer +# else: +# invoice = frappe.get_doc("Purchase Invoice", invoice) +# account = pe.paid_to +# dr_or_cr = "credit" +# rev = "debit" +# against = invoice.credit_to +# party = invoice.supplier +# gl_entries.append( +# pe.get_gl_dict( +# { +# "account": account, +# "party_type": party_type, +# "party": party, +# "due_date": invoice.due_date, +# "against": against, +# dr_or_cr: allocated_amount, +# dr_or_cr + "_in_account_currency": allocated_amount, +# rev: 0, +# rev + "_in_account_currency": 0, +# "cost_center": invoice.cost_center, +# "project": invoice.project, +# "against_voucher_type": "Payment Entry", +# "against_voucher": pe.name, +# }, +# invoice.party_account_currency, +# item=pe, +# ) +# ) - (dr_or_cr, rev) = ("credit", "debit") if party_type == "Customer" else ("debit", "credit") - gl_entries.append( - pe.get_gl_dict( - { - "account": against, - "party_type": party_type, - "party": party, - "due_date": invoice.due_date, - dr_or_cr: allocated_amount, - dr_or_cr + "_in_account_currency": allocated_amount, - rev: 0, - rev + "_in_account_currency": 0, - "cost_center": invoice.cost_center, - "project": invoice.project, - "against_voucher_type": invoice.doctype, - "against_voucher": invoice.name, - }, - invoice.party_account_currency, - item=pe, - ) - ) +# (dr_or_cr, rev) = ("credit", "debit") if party_type == "Customer" else ("debit", "credit") +# gl_entries.append( +# pe.get_gl_dict( +# { +# "account": against, +# "party_type": party_type, +# "party": party, +# "due_date": invoice.due_date, +# dr_or_cr: allocated_amount, +# dr_or_cr + "_in_account_currency": allocated_amount, +# rev: 0, +# rev + "_in_account_currency": 0, +# "cost_center": invoice.cost_center, +# "project": invoice.project, +# "against_voucher_type": invoice.doctype, +# "against_voucher": invoice.name, +# }, +# invoice.party_account_currency, +# item=pe, +# ) +# ) -def check_advance_liability_entry(gl_entries, company, advances, invoice, party_type): - advance_payments_as_liability = frappe.db.get_value( - "Company", {"company_name": company}, "book_advance_payments_as_liability" - ) - if advance_payments_as_liability: - for advance_entry in advances: - make_advance_liability_entry( - gl_entries, - advance_entry.reference_name, - advance_entry.allocated_amount, - invoice=invoice, - party_type=party_type, - ) +# def check_advance_liability_entry(gl_entries, company, advances, invoice, party_type): +# advance_payments_as_liability = frappe.db.get_value( +# "Company", {"company_name": company}, "book_advance_payments_as_liability" +# ) +# if advance_payments_as_liability: +# for advance_entry in advances: +# make_advance_liability_entry( +# gl_entries, +# advance_entry.reference_name, +# advance_entry.allocated_amount, +# invoice=invoice, +# party_type=party_type, +# ) @erpnext.allow_regional diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 408e89b0ef3..c3bd753f1b3 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -39,7 +39,8 @@ frappe.ui.form.on("Customer", { let d = locals[cdt][cdn]; return { filters: { - "account_type": 'Payable', + "account_type": 'Receivable', + "root_type": "Liability", "company": d.company, "is_group": 0 } diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 089e20d4423..333538722ee 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -227,8 +227,8 @@ erpnext.company.setup_queries = function(frm) { ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}], ["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}], ["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}], - ["default_advance_received_account", {"account_type": "Payable"}], - ["default_advance_paid_account", {"account_type": "Receivable"}], + ["default_advance_received_account", {"root_type": "Liability", "account_type": "Receivable"}], + ["default_advance_paid_account", {"root_type": "Asset", "account_type": "Payable"}], ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 611e2ab2210..6292ad7349d 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -71,7 +71,7 @@ "cost_center", "default_finance_book", "advance_payments_section", - "book_advance_payments_as_liability", + "book_advance_payments_in_separate_party_account", "column_break_fwcf", "default_advance_received_account", "default_advance_paid_account", @@ -700,20 +700,13 @@ "no_copy": 1, "options": "Account" }, - { - "default": "0", - "description": "Enabling this option will allow you to record -

1. Advances Received in a Liability Account instead of the Receivable Account

2. Advances Paid in an Asset Account instead of the Payable Account", - "fieldname": "book_advance_payments_as_liability", - "fieldtype": "Check", - "label": "Book Advance Payments in Separate Party Account" - }, { "fieldname": "advance_payments_section", "fieldtype": "Section Break", "label": "Advance Payments" }, { - "depends_on": "eval:doc.book_advance_payments_as_liability", + "depends_on": "eval:doc.book_advance_payments_in_separate_party_account", "fieldname": "default_advance_received_account", "fieldtype": "Link", "label": "Default Advance Received Account", @@ -721,7 +714,7 @@ "options": "Account" }, { - "depends_on": "eval:doc.book_advance_payments_as_liability", + "depends_on": "eval:doc.book_advance_payments_in_separate_party_account", "fieldname": "default_advance_paid_account", "fieldtype": "Link", "label": "Default Advance Paid Account", @@ -731,6 +724,13 @@ { "fieldname": "column_break_fwcf", "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "Enabling this option will allow you to record -

1. Advances Received in a Liability Account instead of the Asset Account

2. Advances Paid in an Asset Account instead of the Liability Account", + "fieldname": "book_advance_payments_in_separate_party_account", + "fieldtype": "Check", + "label": "Book Advance Payments in Separate Party Account" } ], "icon": "fa fa-building", @@ -738,7 +738,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-06-16 13:32:48.790947", + "modified": "2023-06-23 18:22:27.219706", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js index ed9893346cb..49a90f959d0 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.js +++ b/erpnext/setup/doctype/customer_group/customer_group.js @@ -41,6 +41,7 @@ frappe.ui.form.on("Customer Group", { return { filters: { "root_type": 'Liability', + "account_type": "Receivable", "company": locals[cdt][cdn].company, "is_group": 0 } diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js index ac5904f4d27..b2acfd73559 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group.js @@ -41,6 +41,7 @@ frappe.ui.form.on("Supplier Group", { return { filters: { "root_type": 'Asset', + "account_type": "Payable", "company": locals[cdt][cdn].company, "is_group": 0 } From 1894dc8197c568936d839ef50fa65879c7ce9ac7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 23 Jun 2023 21:53:34 +0530 Subject: [PATCH 21/29] fix: Test case and code cleanup --- .../payment_reconciliation.js | 1 + .../purchase_invoice_advance.json | 12 +-- .../doctype/sales_invoice/sales_invoice.py | 3 +- .../sales_invoice_advance.json | 12 +-- erpnext/accounts/utils.py | 6 +- erpnext/controllers/accounts_controller.py | 78 ------------------- 6 files changed, 11 insertions(+), 101 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index bd931f1a2b2..16e3f95ee63 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -34,6 +34,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo filters: { "company": this.frm.doc.company, "is_group": 0, + "account_type": this.frm.doc.party_type == 'Customer' ? "Receivable": "Payable", "root_type": this.frm.doc.party_type == 'Customer' ? "Liability": "Asset" } }; diff --git a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json index 9082115f23d..4db531eac90 100644 --- a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json +++ b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json @@ -14,8 +14,7 @@ "advance_amount", "allocated_amount", "exchange_gain_loss", - "ref_exchange_rate", - "account" + "ref_exchange_rate" ], "fields": [ { @@ -112,20 +111,13 @@ "label": "Reference Exchange Rate", "non_negative": 1, "read_only": 1 - }, - { - "fieldname": "account", - "fieldtype": "Link", - "label": "Account", - "options": "Account", - "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-06-01 16:56:48.530169", + "modified": "2023-06-23 21:13:18.013816", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Advance", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 25553cff0c6..7ab1c893971 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1052,12 +1052,13 @@ class SalesInvoice(SellingController): elif self.docstatus == 2 and cint(self.update_stock) and cint(auto_accounting_for_stock): make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) - def get_gl_entries(self): + def get_gl_entries(self, warehouse_account=None): from erpnext.accounts.general_ledger import merge_similar_entries gl_entries = [] self.make_customer_gl_entry(gl_entries) + self.make_tax_gl_entries(gl_entries) self.make_exchange_gain_loss_gl_entries(gl_entries) self.make_internal_transfer_gl_entries(gl_entries) diff --git a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json index aa52b1cac2d..0ae85d90004 100644 --- a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json +++ b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json @@ -14,8 +14,7 @@ "advance_amount", "allocated_amount", "exchange_gain_loss", - "ref_exchange_rate", - "account" + "ref_exchange_rate" ], "fields": [ { @@ -113,20 +112,13 @@ "label": "Reference Exchange Rate", "non_negative": 1, "read_only": 1 - }, - { - "fieldname": "account", - "fieldtype": "Link", - "label": "Account", - "options": "Account", - "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-05-31 11:47:00.191681", + "modified": "2023-06-23 21:12:57.557731", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Advance", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 5c256b730f3..046711532f0 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -474,7 +474,9 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) gl_map = doc.build_gl_map() create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) - doc.make_advance_gl_entries(entry.against_voucher_type, entry.against_voucher) + + if voucher_type == "Payment Entry": + doc.make_advance_gl_entries(entry.against_voucher_type, entry.against_voucher) # Only update outstanding for newly linked vouchers for entry in entries: @@ -732,7 +734,7 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): for pe in linked_pe: try: - pe_doc = frappe.get_doc("Payment Entry", pe, cache=True) + pe_doc = frappe.get_doc("Payment Entry", pe) pe_doc.set_amounts() pe_doc.make_advance_gl_entries(against_voucher_type=ref_type, against_voucher=ref_no, cancel=1) pe_doc.clear_unallocated_reference_document_rows() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 0d9ab6498db..4193b5327d3 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2916,84 +2916,6 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.create_stock_reservation_entries() -# def make_advance_liability_entry( -# gl_entries, pe, allocated_amount, invoice, party_type -# ): -# pe = frappe.get_doc("Payment Entry", pe) -# if party_type == "Customer": -# invoice = frappe.get_doc("Sales Invoice", invoice) -# account = pe.paid_from -# dr_or_cr = "debit" -# rev = "credit" -# against = invoice.debit_to -# party = invoice.customer -# else: -# invoice = frappe.get_doc("Purchase Invoice", invoice) -# account = pe.paid_to -# dr_or_cr = "credit" -# rev = "debit" -# against = invoice.credit_to -# party = invoice.supplier -# gl_entries.append( -# pe.get_gl_dict( -# { -# "account": account, -# "party_type": party_type, -# "party": party, -# "due_date": invoice.due_date, -# "against": against, -# dr_or_cr: allocated_amount, -# dr_or_cr + "_in_account_currency": allocated_amount, -# rev: 0, -# rev + "_in_account_currency": 0, -# "cost_center": invoice.cost_center, -# "project": invoice.project, -# "against_voucher_type": "Payment Entry", -# "against_voucher": pe.name, -# }, -# invoice.party_account_currency, -# item=pe, -# ) -# ) - -# (dr_or_cr, rev) = ("credit", "debit") if party_type == "Customer" else ("debit", "credit") -# gl_entries.append( -# pe.get_gl_dict( -# { -# "account": against, -# "party_type": party_type, -# "party": party, -# "due_date": invoice.due_date, -# dr_or_cr: allocated_amount, -# dr_or_cr + "_in_account_currency": allocated_amount, -# rev: 0, -# rev + "_in_account_currency": 0, -# "cost_center": invoice.cost_center, -# "project": invoice.project, -# "against_voucher_type": invoice.doctype, -# "against_voucher": invoice.name, -# }, -# invoice.party_account_currency, -# item=pe, -# ) -# ) - - -# def check_advance_liability_entry(gl_entries, company, advances, invoice, party_type): -# advance_payments_as_liability = frappe.db.get_value( -# "Company", {"company_name": company}, "book_advance_payments_as_liability" -# ) -# if advance_payments_as_liability: -# for advance_entry in advances: -# make_advance_liability_entry( -# gl_entries, -# advance_entry.reference_name, -# advance_entry.allocated_amount, -# invoice=invoice, -# party_type=party_type, -# ) - - @erpnext.allow_regional def validate_regional(doc): pass From 62c3ca8286e131d9d4072e9c8d3d298133c755d5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 26 Jun 2023 23:53:55 +0530 Subject: [PATCH 22/29] fix: Paid invoice in AR report --- .../accounts/doctype/payment_entry/payment_entry.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 8141d0519e6..cbf8c0865f3 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -986,12 +986,20 @@ class PaymentEntry(AccountsController): gle = party_gl_dict.copy() 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 + gle.update( { dr_or_cr: allocated_amount_in_company_currency, dr_or_cr + "_in_account_currency": d.allocated_amount, - "against_voucher_type": d.reference_doctype, - "against_voucher": d.reference_name, + "against_voucher_type": against_voucher_type, + "against_voucher": against_voucher, "cost_center": cost_center, } ) From f2edc91dc6ce60fae598f537634430a8da7dd453 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 27 Jun 2023 18:11:47 +0530 Subject: [PATCH 23/29] fix: Multi invoice reconciliation --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 ++----- erpnext/accounts/utils.py | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index cbf8c0865f3..a0f638246f2 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1019,15 +1019,12 @@ class PaymentEntry(AccountsController): gl_entries.append(gle) - def make_advance_gl_entries(self, against_voucher_type=None, against_voucher=None, cancel=0): + def make_advance_gl_entries(self, cancel=0): if self.book_advance_payments_in_separate_party_account: gl_entries = [] for d in self.get("references"): if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"): - 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) + self.make_invoice_liability_entry(gl_entries, d) if cancel: for entry in gl_entries: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 046711532f0..1fa0e86c266 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -476,7 +476,7 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) if voucher_type == "Payment Entry": - doc.make_advance_gl_entries(entry.against_voucher_type, entry.against_voucher) + doc.make_advance_gl_entries() # Only update outstanding for newly linked vouchers for entry in entries: From 7312827d4dd06aa89d338b6eb3d63ed4f8d64c8f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 27 Jun 2023 18:25:10 +0530 Subject: [PATCH 24/29] fix: On cancel flow --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index a0f638246f2..cbf8c0865f3 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1019,12 +1019,15 @@ class PaymentEntry(AccountsController): gl_entries.append(gle) - def make_advance_gl_entries(self, cancel=0): + def make_advance_gl_entries(self, against_voucher_type=None, against_voucher=None, cancel=0): if self.book_advance_payments_in_separate_party_account: gl_entries = [] for d in self.get("references"): if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"): - self.make_invoice_liability_entry(gl_entries, d) + 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) if cancel: for entry in gl_entries: From 1e078d03bb6c8b4abc74254b0daff4edfdcc2c2b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 29 Jun 2023 12:18:25 +0530 Subject: [PATCH 25/29] fix: Partial PLE cancellation --- .../payment_ledger_entry/payment_ledger_entry.json | 8 +++++++- erpnext/accounts/general_ledger.py | 6 +++++- erpnext/accounts/utils.py | 11 ++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json index 22842cec0fe..9cf2ac6c2a4 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -13,6 +13,7 @@ "party_type", "party", "due_date", + "voucher_detail_no", "cost_center", "finance_book", "voucher_type", @@ -142,12 +143,17 @@ "fieldname": "remarks", "fieldtype": "Text", "label": "Remarks" + }, + { + "fieldname": "voucher_detail_no", + "fieldtype": "Data", + "label": "Voucher Detail No" } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2022-08-22 15:32:56.629430", + "modified": "2023-06-29 12:24:20.500632", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Ledger Entry", diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 22f39596e64..f1dad875fa7 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -526,7 +526,11 @@ def make_reverse_gl_entries( if gl_entries: create_payment_ledger_entry( - gl_entries, cancel=1, adv_adj=adv_adj, update_outstanding=update_outstanding + gl_entries, + cancel=1, + adv_adj=adv_adj, + update_outstanding=update_outstanding, + partial_cancel=partial_cancel, ) validate_accounting_period(gl_entries) check_freezing_date(gl_entries[0]["posting_date"], adv_adj) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 1fa0e86c266..2a40a3abafc 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1467,6 +1467,7 @@ def get_payment_ledger_entries(gl_entries, cancel=0): due_date=gle.due_date, 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, @@ -1488,7 +1489,7 @@ def get_payment_ledger_entries(gl_entries, cancel=0): def create_payment_ledger_entry( - gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0 + gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0, partial_cancel=False ): if gl_entries: ple_map = get_payment_ledger_entries(gl_entries, cancel=cancel) @@ -1498,7 +1499,7 @@ def create_payment_ledger_entry( ple = frappe.get_doc(entry) if cancel: - delink_original_entry(ple) + delink_original_entry(ple, partial_cancel=partial_cancel) ple.flags.ignore_permissions = 1 ple.flags.adv_adj = adv_adj @@ -1545,7 +1546,7 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa ref_doc.set_status(update=True) -def delink_original_entry(pl_entry): +def delink_original_entry(pl_entry, partial_cancel=False): if pl_entry: ple = qb.DocType("Payment Ledger Entry") query = ( @@ -1565,6 +1566,10 @@ def delink_original_entry(pl_entry): & (ple.against_voucher_no == pl_entry.against_voucher_no) ) ) + + if partial_cancel: + query = query.where(ple.voucher_detail_no == pl_entry.voucher_detail_no) + query.run() From bbb6ebb84ec5ae2cd360c98ab279e1a3cd0aa202 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 30 Jun 2023 13:25:22 +0530 Subject: [PATCH 26/29] fix: Outstanding amount validation --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 37459e3cf00..c0fd63e9a29 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -420,7 +420,10 @@ class PaymentEntry(AccountsController): elif self.party_type == "Employee": ref_party_account = ref_doc.payable_account - if ref_party_account != self.party_account: + if ( + ref_party_account != self.party_account + and not self.book_advance_payments_in_separate_party_account + ): frappe.throw( _("{0} {1} is associated with {2}, but Party Account is {3}").format( d.reference_doctype, d.reference_name, ref_party_account, self.party_account @@ -1482,7 +1485,7 @@ def get_outstanding_reference_documents(args, validate=False): outstanding_invoices = get_outstanding_invoices( args.get("party_type"), args.get("party"), - args.get("party_account"), + get_party_account(args.get("party_type"), args.get("party"), args.get("company")), common_filter=common_filter, posting_date=posting_and_due_date, min_outstanding=args.get("outstanding_amt_greater_than"), From 0a49213338f6dac2418d5bc789b984293bb1cc84 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 30 Jun 2023 17:32:42 +0530 Subject: [PATCH 27/29] test: Update test records --- erpnext/accounts/doctype/sales_invoice/test_records.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_records.json b/erpnext/accounts/doctype/sales_invoice/test_records.json index 3781f8ccc9b..61e5219c80c 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_records.json +++ b/erpnext/accounts/doctype/sales_invoice/test_records.json @@ -6,7 +6,7 @@ "cost_center": "_Test Cost Center - _TC", "customer": "_Test Customer", "customer_name": "_Test Customer", - "debit_to": "_Test Receivable - _TC", + "debit_to": "Debtors - _TC", "doctype": "Sales Invoice", "items": [ { @@ -78,7 +78,7 @@ "currency": "INR", "customer": "_Test Customer", "customer_name": "_Test Customer", - "debit_to": "_Test Receivable - _TC", + "debit_to": "Debtors - _TC", "doctype": "Sales Invoice", "cost_center": "_Test Cost Center - _TC", "items": [ @@ -137,7 +137,7 @@ "currency": "INR", "customer": "_Test Customer", "customer_name": "_Test Customer", - "debit_to": "_Test Receivable - _TC", + "debit_to": "Debtors - _TC", "doctype": "Sales Invoice", "cost_center": "_Test Cost Center - _TC", "items": [ @@ -265,7 +265,7 @@ "currency": "INR", "customer": "_Test Customer", "customer_name": "_Test Customer", - "debit_to": "_Test Receivable - _TC", + "debit_to": "Debtors - _TC", "doctype": "Sales Invoice", "cost_center": "_Test Cost Center - _TC", "items": [ From 7e7737d6926ed94b891556b7df4e46b1e87673f6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 30 Jun 2023 18:37:52 +0530 Subject: [PATCH 28/29] test: Update test account --- erpnext/accounts/doctype/finance_book/test_finance_book.py | 2 +- .../accounts/doctype/journal_entry/test_journal_entry.py | 4 ++-- erpnext/accounts/doctype/journal_entry/test_records.json | 4 ++-- .../accounts/doctype/sales_invoice/test_sales_invoice.py | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/finance_book/test_finance_book.py b/erpnext/accounts/doctype/finance_book/test_finance_book.py index 7b2575d2c32..42c0e512386 100644 --- a/erpnext/accounts/doctype/finance_book/test_finance_book.py +++ b/erpnext/accounts/doctype/finance_book/test_finance_book.py @@ -13,7 +13,7 @@ class TestFinanceBook(unittest.TestCase): finance_book = create_finance_book() # create jv entry - jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable - _TC", 100, save=False) + jv = make_journal_entry("_Test Bank - _TC", "Debtors - _TC", 100, save=False) jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer"}) diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 73b19115434..e7aca79d08b 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -43,7 +43,7 @@ class TestJournalEntry(unittest.TestCase): frappe.db.sql( """select name from `tabJournal Entry Account` where account = %s and docstatus = 1 and parent = %s""", - ("_Test Receivable - _TC", test_voucher.name), + ("Debtors - _TC", test_voucher.name), ) ) @@ -273,7 +273,7 @@ class TestJournalEntry(unittest.TestCase): jv.submit() # create jv in USD, but account currency in INR - jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable - _TC", 100, save=False) + jv = make_journal_entry("_Test Bank - _TC", "Debtors - _TC", 100, save=False) jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD"}) diff --git a/erpnext/accounts/doctype/journal_entry/test_records.json b/erpnext/accounts/doctype/journal_entry/test_records.json index 5077305cf22..dafcf56abdb 100644 --- a/erpnext/accounts/doctype/journal_entry/test_records.json +++ b/erpnext/accounts/doctype/journal_entry/test_records.json @@ -6,7 +6,7 @@ "doctype": "Journal Entry", "accounts": [ { - "account": "_Test Receivable - _TC", + "account": "Debtors - _TC", "party_type": "Customer", "party": "_Test Customer", "credit_in_account_currency": 400.0, @@ -70,7 +70,7 @@ "doctype": "Journal Entry", "accounts": [ { - "account": "_Test Receivable - _TC", + "account": "Debtors - _TC", "party_type": "Customer", "party": "_Test Customer", "credit_in_account_currency": 0.0, diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 69ddfaac8df..0280c3590c4 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1724,7 +1724,7 @@ class TestSalesInvoice(unittest.TestCase): # Party Account currency must be in USD, as there is existing GLE with USD si4 = create_sales_invoice( customer="_Test Customer USD", - debit_to="_Test Receivable - _TC", + debit_to="Debtors - _TC", currency="USD", conversion_rate=50, do_not_submit=True, @@ -1737,7 +1737,7 @@ class TestSalesInvoice(unittest.TestCase): si3.cancel() si5 = create_sales_invoice( customer="_Test Customer USD", - debit_to="_Test Receivable - _TC", + debit_to="Debtors - _TC", currency="USD", conversion_rate=50, do_not_submit=True, @@ -1816,7 +1816,7 @@ class TestSalesInvoice(unittest.TestCase): "reference_date": nowdate(), "received_amount": 300, "paid_amount": 300, - "paid_from": "_Test Receivable - _TC", + "paid_from": "Debtors - _TC", "paid_to": "_Test Cash - _TC", } ) From 80e6c90740d50919806e76183e061247e7c2d08a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 30 Jun 2023 19:35:22 +0530 Subject: [PATCH 29/29] chore: precision in test --- erpnext/accounts/doctype/payment_entry/test_payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index ae2625b6539..70cc4b3d347 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -932,7 +932,7 @@ class TestPaymentEntry(FrappeTestCase): self.assertEqual(pe.cost_center, si.cost_center) self.assertEqual(flt(expected_account_balance), account_balance) self.assertEqual(flt(expected_party_balance), party_balance) - self.assertEqual(flt(expected_party_account_balance), party_account_balance) + self.assertEqual(flt(expected_party_account_balance, 2), flt(party_account_balance, 2)) def test_multi_currency_payment_entry_with_taxes(self): payment_entry = create_payment_entry(