diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7257e6d75f..a3a7be2958 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -346,6 +346,12 @@ class PaymentEntry(AccountsController): ) ) + 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), + title=_("Invalid Invoice"), + ) + if ref_doc.docstatus != 1: frappe.throw(_("{0} {1} must be submitted").format(d.reference_doctype, d.reference_name)) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 5b70b510d2..a8211c81f1 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -743,6 +743,21 @@ class TestPaymentEntry(unittest.TestCase): flt(payment_entry.total_taxes_and_charges, 2), flt(10 / payment_entry.target_exchange_rate, 2) ) + def test_payment_entry_against_onhold_purchase_invoice(self): + pi = make_purchase_invoice() + + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC") + pe.reference_no = "1" + pe.reference_date = "2016-01-01" + + # block invoice after creating payment entry + # since `get_payment_entry` will not attach blocked invoice to payment + pi.block_invoice() + with self.assertRaises(frappe.ValidationError) as err: + pe.save() + + self.assertTrue("is on hold" in str(err.exception).lower()) + def create_payment_entry(**args): payment_entry = frappe.new_doc("Payment Entry") diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index a0c0eccff1..b8154dd1f9 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3140,6 +3140,39 @@ class TestSalesInvoice(unittest.TestCase): si.reload() self.assertTrue(si.items[0].serial_no) + def test_sales_invoice_with_disabled_account(self): + try: + account = frappe.get_doc("Account", "VAT 5% - _TC") + account.disabled = 1 + account.save() + + si = create_sales_invoice(do_not_save=True) + si.posting_date = add_days(getdate(), 1) + si.taxes = [] + + si.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "VAT 5% - _TC", + "cost_center": "Main - _TC", + "description": "VAT @ 5.0", + "rate": 9, + }, + ) + si.save() + + with self.assertRaises(frappe.ValidationError) as err: + si.submit() + + self.assertTrue( + "Cannot create accounting entries against disabled accounts" in str(err.exception) + ) + + finally: + account.disabled = 0 + account.save() + def test_gain_loss_with_advance_entry(self): from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 89034ebb8c..1598d914e2 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -31,6 +31,7 @@ def make_gl_entries( if gl_map: if not cancel: validate_accounting_period(gl_map) + validate_disabled_accounts(gl_map) gl_map = process_gl_map(gl_map, merge_entries) if gl_map and len(gl_map) > 1: save_entries(gl_map, adv_adj, update_outstanding, from_repost) @@ -45,6 +46,26 @@ def make_gl_entries( make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding) +def validate_disabled_accounts(gl_map): + accounts = [d.account for d in gl_map if d.account] + + Account = frappe.qb.DocType("Account") + + disabled_accounts = ( + frappe.qb.from_(Account) + .where(Account.name.isin(accounts) & Account.disabled == 1) + .select(Account.name, Account.disabled) + ).run(as_dict=True) + + if disabled_accounts: + account_list = "
" + account_list += ", ".join([frappe.bold(d.name) for d in disabled_accounts]) + frappe.throw( + _("Cannot create accounting entries against disabled accounts: {0}").format(account_list), + title=_("Disabled Account Selected"), + ) + + def validate_accounting_period(gl_map): accounting_periods = frappe.db.sql( """ SELECT diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index abe9977c84..eeb5a7f1a9 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -162,6 +162,7 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters): {account_type_condition} AND is_group = 0 AND company = %(company)s + AND disabled = %(disabled)s AND (account_currency = %(currency)s or ifnull(account_currency, '') = '') AND `{searchfield}` LIKE %(txt)s {mcond} @@ -175,6 +176,7 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters): dict( account_types=filters.get("account_type"), company=filters.get("company"), + disabled=filters.get("disabled", 0), currency=company_currency, txt="%{}%".format(txt), offset=start, diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 84c717676c..c1fe72bb48 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -27,7 +27,8 @@ frappe.ui.form.on(cur_frm.doctype, { query: "erpnext.controllers.queries.tax_account_query", filters: { "account_type": account_type, - "company": doc.company + "company": doc.company, + "disabled": 0 } } });