Merge pull request #37194 from GursheenK/editable-purchase-invoice
feat: editable purchase invoice
This commit is contained in:
commit
796cc0915a
@ -65,6 +65,25 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
|
|||||||
erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
|
erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.frm.doc.repost_required && this.frm.doc.docstatus===1) {
|
||||||
|
this.frm.set_intro(__("Accounting entries for this invoice need to be reposted. Please click on 'Repost' button to update."));
|
||||||
|
this.frm.add_custom_button(__('Repost Accounting Entries'),
|
||||||
|
() => {
|
||||||
|
this.frm.call({
|
||||||
|
doc: this.frm.doc,
|
||||||
|
method: 'repost_accounting_entries',
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: __('Reposting...'),
|
||||||
|
callback: (r) => {
|
||||||
|
if (!r.exc) {
|
||||||
|
frappe.msgprint(__('Accounting Entries are reposted.'));
|
||||||
|
me.frm.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).removeClass('btn-default').addClass('btn-warning');
|
||||||
|
}
|
||||||
|
|
||||||
if(!doc.is_return && doc.docstatus == 1 && doc.outstanding_amount != 0){
|
if(!doc.is_return && doc.docstatus == 1 && doc.outstanding_amount != 0){
|
||||||
if(doc.on_hold) {
|
if(doc.on_hold) {
|
||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
|
@ -166,6 +166,7 @@
|
|||||||
"against_expense_account",
|
"against_expense_account",
|
||||||
"column_break_63",
|
"column_break_63",
|
||||||
"unrealized_profit_loss_account",
|
"unrealized_profit_loss_account",
|
||||||
|
"repost_required",
|
||||||
"subscription_section",
|
"subscription_section",
|
||||||
"subscription",
|
"subscription",
|
||||||
"auto_repeat",
|
"auto_repeat",
|
||||||
@ -191,8 +192,7 @@
|
|||||||
"inter_company_invoice_reference",
|
"inter_company_invoice_reference",
|
||||||
"is_old_subcontracting_flow",
|
"is_old_subcontracting_flow",
|
||||||
"remarks",
|
"remarks",
|
||||||
"connections_tab",
|
"connections_tab"
|
||||||
"column_break_38"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -990,6 +990,7 @@
|
|||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
"fieldname": "cash_bank_account",
|
"fieldname": "cash_bank_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Cash/Bank Account",
|
"label": "Cash/Bank Account",
|
||||||
@ -1053,6 +1054,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
"depends_on": "eval:flt(doc.write_off_amount)!=0",
|
"depends_on": "eval:flt(doc.write_off_amount)!=0",
|
||||||
"fieldname": "write_off_account",
|
"fieldname": "write_off_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -1217,6 +1219,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
"default": "No",
|
"default": "No",
|
||||||
"fieldname": "is_opening",
|
"fieldname": "is_opening",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
@ -1349,6 +1352,7 @@
|
|||||||
"options": "Project"
|
"options": "Project"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
"depends_on": "eval:doc.is_internal_supplier",
|
"depends_on": "eval:doc.is_internal_supplier",
|
||||||
"description": "Unrealized Profit/Loss account for intra-company transfers",
|
"description": "Unrealized Profit/Loss account for intra-company transfers",
|
||||||
"fieldname": "unrealized_profit_loss_account",
|
"fieldname": "unrealized_profit_loss_account",
|
||||||
@ -1504,10 +1508,6 @@
|
|||||||
"fieldname": "column_break_6",
|
"fieldname": "column_break_6",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "column_break_38",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_50",
|
"fieldname": "column_break_50",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
@ -1578,13 +1578,22 @@
|
|||||||
"fieldname": "use_company_roundoff_cost_center",
|
"fieldname": "use_company_roundoff_cost_center",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Use Company Default Round Off Cost Center"
|
"label": "Use Company Default Round Off Cost Center"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "repost_required",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Repost Required",
|
||||||
|
"options": "Account",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 204,
|
"idx": 204,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-07-25 17:22:59.145031",
|
"modified": "2023-09-21 12:22:04.545106",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
@ -11,6 +11,9 @@ from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate,
|
|||||||
import erpnext
|
import erpnext
|
||||||
from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
||||||
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
||||||
|
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
|
||||||
|
validate_docs_for_deferred_accounting,
|
||||||
|
)
|
||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
|
||||||
check_if_return_invoice_linked_with_payment_entry,
|
check_if_return_invoice_linked_with_payment_entry,
|
||||||
get_total_in_party_account_currency,
|
get_total_in_party_account_currency,
|
||||||
@ -484,6 +487,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
_("Stock cannot be updated against Purchase Receipt {0}").format(item.purchase_receipt)
|
_("Stock cannot be updated against Purchase Receipt {0}").format(item.purchase_receipt)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def validate_for_repost(self):
|
||||||
|
self.validate_write_off_account()
|
||||||
|
self.validate_expense_account()
|
||||||
|
validate_docs_for_deferred_accounting([], [self.name])
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
super(PurchaseInvoice, self).on_submit()
|
super(PurchaseInvoice, self).on_submit()
|
||||||
|
|
||||||
@ -522,6 +530,18 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
self.process_common_party_accounting()
|
self.process_common_party_accounting()
|
||||||
|
|
||||||
|
def on_update_after_submit(self):
|
||||||
|
if hasattr(self, "repost_required"):
|
||||||
|
fields_to_check = [
|
||||||
|
"cash_bank_account",
|
||||||
|
"write_off_account",
|
||||||
|
"unrealized_profit_loss_account",
|
||||||
|
]
|
||||||
|
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
|
||||||
|
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
|
||||||
|
self.validate_for_repost()
|
||||||
|
self.db_set("repost_required", self.needs_repost)
|
||||||
|
|
||||||
def make_gl_entries(self, gl_entries=None, from_repost=False):
|
def make_gl_entries(self, gl_entries=None, from_repost=False):
|
||||||
if not gl_entries:
|
if not gl_entries:
|
||||||
gl_entries = self.get_gl_entries()
|
gl_entries = self.get_gl_entries()
|
||||||
|
@ -1744,7 +1744,6 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
|
|||||||
|
|
||||||
pi = make_purchase_invoice(
|
pi = make_purchase_invoice(
|
||||||
company="_Test Company",
|
company="_Test Company",
|
||||||
customer="_Test Supplier",
|
|
||||||
do_not_save=True,
|
do_not_save=True,
|
||||||
do_not_submit=True,
|
do_not_submit=True,
|
||||||
rate=1000,
|
rate=1000,
|
||||||
@ -1862,7 +1861,6 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
|
|||||||
|
|
||||||
pi = make_purchase_invoice(
|
pi = make_purchase_invoice(
|
||||||
company="_Test Company",
|
company="_Test Company",
|
||||||
customer="_Test Supplier",
|
|
||||||
do_not_save=True,
|
do_not_save=True,
|
||||||
do_not_submit=True,
|
do_not_submit=True,
|
||||||
rate=1000,
|
rate=1000,
|
||||||
@ -1892,6 +1890,32 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
|
|||||||
clear_dimension_defaults("Branch")
|
clear_dimension_defaults("Branch")
|
||||||
disable_dimension()
|
disable_dimension()
|
||||||
|
|
||||||
|
def test_repost_accounting_entries(self):
|
||||||
|
pi = make_purchase_invoice(
|
||||||
|
rate=1000,
|
||||||
|
price_list_rate=1000,
|
||||||
|
qty=1,
|
||||||
|
)
|
||||||
|
expected_gle = [
|
||||||
|
["_Test Account Cost for Goods Sold - _TC", 1000, 0.0, nowdate()],
|
||||||
|
["Creditors - _TC", 0.0, 1000, nowdate()],
|
||||||
|
]
|
||||||
|
check_gl_entries(self, pi.name, expected_gle, nowdate())
|
||||||
|
|
||||||
|
pi.items[0].expense_account = "Service - _TC"
|
||||||
|
pi.save()
|
||||||
|
pi.load_from_db()
|
||||||
|
self.assertTrue(pi.repost_required)
|
||||||
|
pi.repost_accounting_entries()
|
||||||
|
|
||||||
|
expected_gle = [
|
||||||
|
["Creditors - _TC", 0.0, 1000, nowdate()],
|
||||||
|
["Service - _TC", 1000, 0.0, nowdate()],
|
||||||
|
]
|
||||||
|
check_gl_entries(self, pi.name, expected_gle, nowdate())
|
||||||
|
pi.load_from_db()
|
||||||
|
self.assertFalse(pi.repost_required)
|
||||||
|
|
||||||
|
|
||||||
def set_advance_flag(company, flag, default_account):
|
def set_advance_flag(company, flag, default_account):
|
||||||
frappe.db.set_value(
|
frappe.db.set_value(
|
||||||
|
@ -473,6 +473,7 @@
|
|||||||
"label": "Accounting"
|
"label": "Accounting"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
"fieldname": "expense_account",
|
"fieldname": "expense_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Expense Head",
|
"label": "Expense Head",
|
||||||
|
@ -86,6 +86,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fieldname": "account_head",
|
"fieldname": "account_head",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -97,6 +98,7 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
"default": ":Company",
|
"default": ":Company",
|
||||||
"fieldname": "cost_center",
|
"fieldname": "cost_center",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-07-27 15:47:58.975034",
|
"modified": "2023-09-26 14:21:27.362567",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Repost Accounting Ledger",
|
"name": "Repost Accounting Ledger",
|
||||||
@ -77,5 +77,6 @@
|
|||||||
],
|
],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": []
|
"states": [],
|
||||||
|
"track_changes": 1
|
||||||
}
|
}
|
@ -21,29 +21,8 @@ class RepostAccountingLedger(Document):
|
|||||||
|
|
||||||
def validate_for_deferred_accounting(self):
|
def validate_for_deferred_accounting(self):
|
||||||
sales_docs = [x.voucher_no for x in self.vouchers if x.voucher_type == "Sales Invoice"]
|
sales_docs = [x.voucher_no for x in self.vouchers if x.voucher_type == "Sales Invoice"]
|
||||||
docs_with_deferred_revenue = frappe.db.get_all(
|
|
||||||
"Sales Invoice Item",
|
|
||||||
filters={"parent": ["in", sales_docs], "docstatus": 1, "enable_deferred_revenue": True},
|
|
||||||
fields=["parent"],
|
|
||||||
as_list=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
purchase_docs = [x.voucher_no for x in self.vouchers if x.voucher_type == "Purchase Invoice"]
|
purchase_docs = [x.voucher_no for x in self.vouchers if x.voucher_type == "Purchase Invoice"]
|
||||||
docs_with_deferred_expense = frappe.db.get_all(
|
validate_docs_for_deferred_accounting(sales_docs, purchase_docs)
|
||||||
"Purchase Invoice Item",
|
|
||||||
filters={"parent": ["in", purchase_docs], "docstatus": 1, "enable_deferred_expense": 1},
|
|
||||||
fields=["parent"],
|
|
||||||
as_list=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
if docs_with_deferred_revenue or docs_with_deferred_expense:
|
|
||||||
frappe.throw(
|
|
||||||
_("Documents: {0} have deferred revenue/expense enabled for them. Cannot repost.").format(
|
|
||||||
frappe.bold(
|
|
||||||
comma_and([x[0] for x in docs_with_deferred_expense + docs_with_deferred_revenue])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def validate_for_closed_fiscal_year(self):
|
def validate_for_closed_fiscal_year(self):
|
||||||
if self.vouchers:
|
if self.vouchers:
|
||||||
@ -139,6 +118,7 @@ class RepostAccountingLedger(Document):
|
|||||||
return rendered_page
|
return rendered_page
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
|
if len(self.vouchers) > 1:
|
||||||
job_name = "repost_accounting_ledger_" + self.name
|
job_name = "repost_accounting_ledger_" + self.name
|
||||||
frappe.enqueue(
|
frappe.enqueue(
|
||||||
method="erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger.start_repost",
|
method="erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger.start_repost",
|
||||||
@ -147,6 +127,8 @@ class RepostAccountingLedger(Document):
|
|||||||
job_name=job_name,
|
job_name=job_name,
|
||||||
)
|
)
|
||||||
frappe.msgprint(_("Repost has started in the background"))
|
frappe.msgprint(_("Repost has started in the background"))
|
||||||
|
else:
|
||||||
|
start_repost(self.name)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@ -181,3 +163,26 @@ def start_repost(account_repost_doc=str) -> None:
|
|||||||
doc.make_gl_entries()
|
doc.make_gl_entries()
|
||||||
|
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def validate_docs_for_deferred_accounting(sales_docs, purchase_docs):
|
||||||
|
docs_with_deferred_revenue = frappe.db.get_all(
|
||||||
|
"Sales Invoice Item",
|
||||||
|
filters={"parent": ["in", sales_docs], "docstatus": 1, "enable_deferred_revenue": True},
|
||||||
|
fields=["parent"],
|
||||||
|
as_list=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
docs_with_deferred_expense = frappe.db.get_all(
|
||||||
|
"Purchase Invoice Item",
|
||||||
|
filters={"parent": ["in", purchase_docs], "docstatus": 1, "enable_deferred_expense": 1},
|
||||||
|
fields=["parent"],
|
||||||
|
as_list=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
if docs_with_deferred_revenue or docs_with_deferred_expense:
|
||||||
|
frappe.throw(
|
||||||
|
_("Documents: {0} have deferred revenue/expense enabled for them. Cannot repost.").format(
|
||||||
|
frappe.bold(comma_and([x[0] for x in docs_with_deferred_expense + docs_with_deferred_revenue]))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ -99,7 +99,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-11-08 07:38:40.079038",
|
"modified": "2023-09-26 14:21:35.719727",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Repost Payment Ledger",
|
"name": "Repost Payment Ledger",
|
||||||
@ -155,5 +155,6 @@
|
|||||||
],
|
],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": []
|
"states": [],
|
||||||
|
"track_changes": 1
|
||||||
}
|
}
|
@ -11,13 +11,13 @@ from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form
|
|||||||
|
|
||||||
import erpnext
|
import erpnext
|
||||||
from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
||||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
|
||||||
get_accounting_dimensions,
|
|
||||||
)
|
|
||||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
|
from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
|
||||||
get_loyalty_program_details_with_points,
|
get_loyalty_program_details_with_points,
|
||||||
validate_loyalty_points,
|
validate_loyalty_points,
|
||||||
)
|
)
|
||||||
|
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
|
||||||
|
validate_docs_for_deferred_accounting,
|
||||||
|
)
|
||||||
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
|
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
|
||||||
get_party_tax_withholding_details,
|
get_party_tax_withholding_details,
|
||||||
)
|
)
|
||||||
@ -168,6 +168,12 @@ class SalesInvoice(SellingController):
|
|||||||
self.validate_account_for_change_amount()
|
self.validate_account_for_change_amount()
|
||||||
self.validate_income_account()
|
self.validate_income_account()
|
||||||
|
|
||||||
|
def validate_for_repost(self):
|
||||||
|
self.validate_write_off_account()
|
||||||
|
self.validate_account_for_change_amount()
|
||||||
|
self.validate_income_account()
|
||||||
|
validate_docs_for_deferred_accounting([self.name], [])
|
||||||
|
|
||||||
def validate_fixed_asset(self):
|
def validate_fixed_asset(self):
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if d.is_fixed_asset and d.meta.get_field("asset") and d.asset:
|
if d.is_fixed_asset and d.meta.get_field("asset") and d.asset:
|
||||||
@ -517,90 +523,21 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
def on_update_after_submit(self):
|
def on_update_after_submit(self):
|
||||||
if hasattr(self, "repost_required"):
|
if hasattr(self, "repost_required"):
|
||||||
needs_repost = 0
|
fields_to_check = [
|
||||||
|
|
||||||
# Check if any field affecting accounting entry is altered
|
|
||||||
doc_before_update = self.get_doc_before_save()
|
|
||||||
accounting_dimensions = get_accounting_dimensions() + ["cost_center", "project"]
|
|
||||||
|
|
||||||
# Check if opening entry check updated
|
|
||||||
if doc_before_update.get("is_opening") != self.is_opening:
|
|
||||||
needs_repost = 1
|
|
||||||
|
|
||||||
if not needs_repost:
|
|
||||||
# Parent Level Accounts excluding party account
|
|
||||||
for field in (
|
|
||||||
"additional_discount_account",
|
"additional_discount_account",
|
||||||
"cash_bank_account",
|
"cash_bank_account",
|
||||||
"account_for_change_amount",
|
"account_for_change_amount",
|
||||||
"write_off_account",
|
"write_off_account",
|
||||||
"loyalty_redemption_account",
|
"loyalty_redemption_account",
|
||||||
"unrealized_profit_loss_account",
|
"unrealized_profit_loss_account",
|
||||||
):
|
]
|
||||||
if doc_before_update.get(field) != self.get(field):
|
child_tables = {
|
||||||
needs_repost = 1
|
"items": ("income_account", "expense_account", "discount_account"),
|
||||||
break
|
"taxes": ("account_head",),
|
||||||
|
}
|
||||||
# Check for parent accounting dimensions
|
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
|
||||||
for dimension in accounting_dimensions:
|
self.validate_for_repost()
|
||||||
if doc_before_update.get(dimension) != self.get(dimension):
|
self.db_set("repost_required", self.needs_repost)
|
||||||
needs_repost = 1
|
|
||||||
break
|
|
||||||
|
|
||||||
# Check for child tables
|
|
||||||
if self.check_if_child_table_updated(
|
|
||||||
"items",
|
|
||||||
doc_before_update,
|
|
||||||
("income_account", "expense_account", "discount_account"),
|
|
||||||
accounting_dimensions,
|
|
||||||
):
|
|
||||||
needs_repost = 1
|
|
||||||
|
|
||||||
if self.check_if_child_table_updated(
|
|
||||||
"taxes", doc_before_update, ("account_head",), accounting_dimensions
|
|
||||||
):
|
|
||||||
needs_repost = 1
|
|
||||||
|
|
||||||
self.validate_accounts()
|
|
||||||
|
|
||||||
# validate if deferred revenue is enabled for any item
|
|
||||||
# Don't allow to update the invoice if deferred revenue is enabled
|
|
||||||
if needs_repost:
|
|
||||||
for item in self.get("items"):
|
|
||||||
if item.enable_deferred_revenue:
|
|
||||||
frappe.throw(
|
|
||||||
_(
|
|
||||||
"Deferred Revenue is enabled for item {0}. You cannot update the invoice after submission."
|
|
||||||
).format(item.item_code)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.db_set("repost_required", needs_repost)
|
|
||||||
|
|
||||||
def check_if_child_table_updated(
|
|
||||||
self, child_table, doc_before_update, fields_to_check, accounting_dimensions
|
|
||||||
):
|
|
||||||
# Check if any field affecting accounting entry is altered
|
|
||||||
for index, item in enumerate(self.get(child_table)):
|
|
||||||
for field in fields_to_check:
|
|
||||||
if doc_before_update.get(child_table)[index].get(field) != item.get(field):
|
|
||||||
return True
|
|
||||||
|
|
||||||
for dimension in accounting_dimensions:
|
|
||||||
if doc_before_update.get(child_table)[index].get(dimension) != item.get(dimension):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def repost_accounting_entries(self):
|
|
||||||
if self.repost_required:
|
|
||||||
self.docstatus = 2
|
|
||||||
self.make_gl_entries_on_cancel()
|
|
||||||
self.docstatus = 1
|
|
||||||
self.make_gl_entries()
|
|
||||||
self.db_set("repost_required", 0)
|
|
||||||
else:
|
|
||||||
frappe.throw(_("No updates pending for reposting"))
|
|
||||||
|
|
||||||
def set_paid_amount(self):
|
def set_paid_amount(self):
|
||||||
paid_amount = 0.0
|
paid_amount = 0.0
|
||||||
|
@ -243,13 +243,38 @@ class AccountsController(TransactionBase):
|
|||||||
_doc.cancel()
|
_doc.cancel()
|
||||||
_doc.delete()
|
_doc.delete()
|
||||||
|
|
||||||
def on_trash(self):
|
def _remove_references_in_repost_doctypes(self):
|
||||||
# delete references in 'Repost Payment Ledger'
|
repost_doctypes = ["Repost Payment Ledger Items", "Repost Accounting Ledger Items"]
|
||||||
rpi = frappe.qb.DocType("Repost Payment Ledger Items")
|
|
||||||
frappe.qb.from_(rpi).delete().where(
|
|
||||||
(rpi.voucher_type == self.doctype) & (rpi.voucher_no == self.name)
|
|
||||||
).run()
|
|
||||||
|
|
||||||
|
for _doctype in repost_doctypes:
|
||||||
|
dt = frappe.qb.DocType(_doctype)
|
||||||
|
rows = (
|
||||||
|
frappe.qb.from_(dt)
|
||||||
|
.select(dt.name, dt.parent, dt.parenttype)
|
||||||
|
.where((dt.voucher_type == self.doctype) & (dt.voucher_no == self.name))
|
||||||
|
.run(as_dict=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
if rows:
|
||||||
|
references_map = frappe._dict()
|
||||||
|
for x in rows:
|
||||||
|
references_map.setdefault((x.parenttype, x.parent), []).append(x.name)
|
||||||
|
|
||||||
|
for doc, rows in references_map.items():
|
||||||
|
repost_doc = frappe.get_doc(doc[0], doc[1])
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
if _doctype == "Repost Payment Ledger Items":
|
||||||
|
repost_doc.remove(repost_doc.get("repost_vouchers", {"name": row})[0])
|
||||||
|
else:
|
||||||
|
repost_doc.remove(repost_doc.get("vouchers", {"name": row})[0])
|
||||||
|
|
||||||
|
repost_doc.flags.ignore_validate_update_after_submit = True
|
||||||
|
repost_doc.flags.ignore_links = True
|
||||||
|
repost_doc.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
def on_trash(self):
|
||||||
|
self._remove_references_in_repost_doctypes()
|
||||||
self._remove_references_in_unreconcile()
|
self._remove_references_in_unreconcile()
|
||||||
|
|
||||||
# delete sl and gl entries on deletion of transaction
|
# delete sl and gl entries on deletion of transaction
|
||||||
@ -2186,6 +2211,45 @@ class AccountsController(TransactionBase):
|
|||||||
_("Select finance book for the item {0} at row {1}").format(item.item_code, item.idx)
|
_("Select finance book for the item {0} at row {1}").format(item.item_code, item.idx)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def check_if_fields_updated(self, fields_to_check, child_tables):
|
||||||
|
# Check if any field affecting accounting entry is altered
|
||||||
|
doc_before_update = self.get_doc_before_save()
|
||||||
|
accounting_dimensions = get_accounting_dimensions() + ["cost_center", "project"]
|
||||||
|
|
||||||
|
# Check if opening entry check updated
|
||||||
|
needs_repost = doc_before_update.get("is_opening") != self.is_opening
|
||||||
|
|
||||||
|
if not needs_repost:
|
||||||
|
# Parent Level Accounts excluding party account
|
||||||
|
fields_to_check += accounting_dimensions
|
||||||
|
for field in fields_to_check:
|
||||||
|
if doc_before_update.get(field) != self.get(field):
|
||||||
|
needs_repost = 1
|
||||||
|
break
|
||||||
|
|
||||||
|
if not needs_repost:
|
||||||
|
# Check for child tables
|
||||||
|
for table in child_tables:
|
||||||
|
needs_repost = check_if_child_table_updated(
|
||||||
|
doc_before_update.get(table), self.get(table), child_tables[table]
|
||||||
|
)
|
||||||
|
if needs_repost:
|
||||||
|
break
|
||||||
|
|
||||||
|
return needs_repost
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def repost_accounting_entries(self):
|
||||||
|
if self.repost_required:
|
||||||
|
repost_ledger = frappe.new_doc("Repost Accounting Ledger")
|
||||||
|
repost_ledger.company = self.company
|
||||||
|
repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name})
|
||||||
|
repost_ledger.insert()
|
||||||
|
repost_ledger.submit()
|
||||||
|
self.db_set("repost_required", 0)
|
||||||
|
else:
|
||||||
|
frappe.throw(_("No updates pending for reposting"))
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_tax_rate(account_head):
|
def get_tax_rate(account_head):
|
||||||
@ -3191,6 +3255,23 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
|||||||
parent.create_stock_reservation_entries()
|
parent.create_stock_reservation_entries()
|
||||||
|
|
||||||
|
|
||||||
|
def check_if_child_table_updated(
|
||||||
|
child_table_before_update, child_table_after_update, fields_to_check
|
||||||
|
):
|
||||||
|
accounting_dimensions = get_accounting_dimensions() + ["cost_center", "project"]
|
||||||
|
# Check if any field affecting accounting entry is altered
|
||||||
|
for index, item in enumerate(child_table_after_update):
|
||||||
|
for field in fields_to_check:
|
||||||
|
if child_table_before_update[index].get(field) != item.get(field):
|
||||||
|
return True
|
||||||
|
|
||||||
|
for dimension in accounting_dimensions:
|
||||||
|
if child_table_before_update[index].get(dimension) != item.get(dimension):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
@erpnext.allow_regional
|
@erpnext.allow_regional
|
||||||
def validate_regional(doc):
|
def validate_regional(doc):
|
||||||
pass
|
pass
|
||||||
|
Loading…
x
Reference in New Issue
Block a user