diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 2b01e2411d66ac765b577f2c0b3ffc3fe7234ea9..d29e7b9969f0860a5b475fdb130811f57f7a48af 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -11,6 +11,10 @@ from frappe.model import core_doctypes_list
from frappe.model.document import Document
from frappe.utils import cstr
+from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
+ get_allowed_types_from_settings,
+)
+
class AccountingDimension(Document):
def before_insert(self):
@@ -86,6 +90,7 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
doc_count = len(get_accounting_dimensions())
count = 0
+ repostable_doctypes = get_allowed_types_from_settings()
for doctype in doclist:
@@ -101,6 +106,7 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
"options": doc.document_type,
"insert_after": insert_after_field,
"owner": "Administrator",
+ "allow_on_submit": 1 if doctype in repostable_doctypes else 0,
}
meta = frappe.get_meta(doctype, cached=False)
diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
index 84ee592d4c0d6e4de9731eab576a9911cc6669ba..72d5cbcfdae474a49d40ae4513d93e5bc0c7e830 100644
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
@@ -44,6 +44,7 @@ def create_bank_account(
return bank_account.name
+
def create_gl_account(gl_account_name="_Test Bank - _TC"):
gl_account = frappe.get_doc(
{
@@ -137,6 +138,7 @@ def add_payments():
pass
pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690)
+
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe.reference_no = "Conrad Oct 18"
pe.reference_date = "2018-10-24"
@@ -227,6 +229,7 @@ def add_payments():
mode_of_payment.append(
"accounts", {"company": "_Test Company", "default_account": "_Test Bank - _TC"}
)
+
mode_of_payment.save()
si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_save=1)
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index c2fc905d96f8be5f05f5d5adc48556bab2e00f6e..eb872be1cff33788184b95d5ec30efe34f989a0a 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -43,6 +43,25 @@ frappe.ui.form.on("Journal Entry", {
refresh: function(frm) {
erpnext.toggle_naming_series();
+ if (frm.doc.repost_required && frm.doc.docstatus===1) {
+ frm.set_intro(__("Accounting entries for this Journal Entry need to be reposted. Please click on 'Repost' button to update."));
+ frm.add_custom_button(__('Repost Accounting Entries'),
+ () => {
+ frm.call({
+ doc: frm.doc,
+ method: 'repost_accounting_entries',
+ freeze: true,
+ freeze_message: __('Reposting...'),
+ callback: (r) => {
+ if (!r.exc) {
+ frappe.msgprint(__('Accounting Entries are reposted.'));
+ frm.refresh();
+ }
+ }
+ });
+ }).removeClass('btn-default').addClass('btn-warning');
+ }
+
if(frm.doc.docstatus > 0) {
frm.add_custom_button(__('Ledger'), function() {
frappe.route_options = {
@@ -246,7 +265,6 @@ var update_jv_details = function(doc, r) {
$.each(r, function(i, d) {
var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
frappe.model.set_value(row.doctype, row.name, "account", d.account)
- frappe.model.set_value(row.doctype, row.name, "balance", d.balance)
});
refresh_field("accounts");
}
@@ -255,7 +273,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
onload() {
this.load_defaults();
this.setup_queries();
- this.setup_balance_formatter();
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
}
@@ -359,19 +376,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
}
- setup_balance_formatter() {
- const formatter = function(value, df, options, doc) {
- var currency = frappe.meta.get_field_currency(df, doc);
- var dr_or_cr = value ? ('') : "";
- return "
"
- + ((value==null || value==="") ? "" : format_currency(Math.abs(value), currency))
- + " " + dr_or_cr
- + "
";
- };
- this.frm.fields_dict.accounts.grid.update_docfield_property('balance', 'formatter', formatter);
- this.frm.fields_dict.accounts.grid.update_docfield_property('party_balance', 'formatter', formatter);
- }
-
reference_name(doc, cdt, cdn) {
var d = frappe.get_doc(cdt, cdn);
@@ -467,23 +471,22 @@ frappe.ui.form.on("Journal Entry Account", {
if(!d.account && d.party_type && d.party) {
if(!frm.doc.company) frappe.throw(__("Please select Company"));
return frm.call({
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_balance",
+ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_currency",
child: d,
args: {
company: frm.doc.company,
party_type: d.party_type,
party: d.party,
- cost_center: d.cost_center
}
});
}
},
cost_center: function(frm, dt, dn) {
- erpnext.journal_entry.set_account_balance(frm, dt, dn);
+ erpnext.journal_entry.set_account_details(frm, dt, dn);
},
account: function(frm, dt, dn) {
- erpnext.journal_entry.set_account_balance(frm, dt, dn);
+ erpnext.journal_entry.set_account_details(frm, dt, dn);
},
debit_in_account_currency: function(frm, cdt, cdn) {
@@ -672,14 +675,14 @@ $.extend(erpnext.journal_entry, {
});
$.extend(erpnext.journal_entry, {
- set_account_balance: function(frm, dt, dn) {
+ set_account_details: function(frm, dt, dn) {
var d = locals[dt][dn];
if(d.account) {
if(!frm.doc.company) frappe.throw(__("Please select Company first"));
if(!frm.doc.posting_date) frappe.throw(__("Please select Posting Date first"));
return frappe.call({
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type",
+ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_details_and_party_type",
args: {
account: d.account,
date: frm.doc.posting_date,
@@ -687,7 +690,6 @@ $.extend(erpnext.journal_entry, {
debit: flt(d.debit_in_account_currency),
credit: flt(d.credit_in_account_currency),
exchange_rate: d.exchange_rate,
- cost_center: d.cost_center
},
callback: function(r) {
if(r.message) {
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json
index fd9b218069a627de9d0afd64292a1ea829a211d9..915a5f79868dec50ab2107e0bc262b1a305ae45a 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.json
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json
@@ -66,7 +66,8 @@
"stock_entry",
"subscription_section",
"auto_repeat",
- "amended_from"
+ "amended_from",
+ "repost_required"
],
"fields": [
{
@@ -565,6 +566,15 @@
"label": "Is System Generated",
"no_copy": 1,
"read_only": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "repost_required",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Repost Required",
+ "print_hide": 1,
+ "read_only": 1
}
],
"icon": "uil uil-file-alt",
@@ -580,7 +590,7 @@
"table_fieldname": "payment_entries"
}
],
- "modified": "2023-12-28 17:19:46.544879",
+ "modified": "2023-10-12 12:32:34.234167",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 49579c51526ebaf736db9677733d2f46705b2f38..12db5753a1f3c114eaaaa333f30b4feb9604c5bb 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -13,6 +13,10 @@ from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import (
get_party_account_based_on_invoice_discounting,
)
+from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
+ validate_docs_for_deferred_accounting,
+ validate_docs_for_voucher_types,
+)
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
get_party_tax_withholding_details,
)
@@ -143,7 +147,6 @@ class JournalEntry(AccountsController):
self.set_print_format_fields()
self.validate_credit_debit_note()
self.validate_empty_accounts_table()
- self.set_account_and_party_balance()
self.validate_inter_company_accounts()
self.validate_accounting_journals()
self.validate_depr_entry_voucher_type()
@@ -154,6 +157,24 @@ class JournalEntry(AccountsController):
if not self.title:
self.title = self.get_title()
+ def validate_for_repost(self):
+ validate_docs_for_voucher_types(["Journal Entry"])
+ validate_docs_for_deferred_accounting([self.name], [])
+
+ def submit(self):
+ if len(self.accounts) > 100:
+ msgprint(_("The task has been enqueued as a background job."), alert=True)
+ self.queue_action("submit", timeout=4600)
+ else:
+ return self._submit()
+
+ def cancel(self):
+ if len(self.accounts) > 100:
+ msgprint(_("The task has been enqueued as a background job."), alert=True)
+ self.queue_action("cancel", timeout=4600)
+ else:
+ return self._cancel()
+
def on_submit(self):
self.validate_cheque_info()
self.check_credit_limit()
@@ -164,6 +185,15 @@ class JournalEntry(AccountsController):
self.update_invoice_discounting()
self.update_unreconciled_amount()
+ def on_update_after_submit(self):
+ if hasattr(self, "repost_required"):
+ self.needs_repost = self.check_if_fields_updated(
+ fields_to_check=[], child_tables={"accounts": []}
+ )
+ if self.needs_repost:
+ self.validate_for_repost()
+ self.db_set("repost_required", self.needs_repost)
+
def on_cancel(self):
# References for this Journal are removed on the `on_cancel` event in accounts_controller
super(JournalEntry, self).on_cancel()
@@ -1149,21 +1179,6 @@ class JournalEntry(AccountsController):
if not self.get("accounts"):
frappe.throw(_("Accounts table cannot be blank."))
- def set_account_and_party_balance(self):
- account_balance = {}
- party_balance = {}
- for d in self.get("accounts"):
- if d.account not in account_balance:
- account_balance[d.account] = get_balance_on(account=d.account, date=self.posting_date)
-
- if (d.party_type, d.party) not in party_balance:
- party_balance[(d.party_type, d.party)] = get_balance_on(
- party_type=d.party_type, party=d.party, date=self.posting_date, company=self.company
- )
-
- d.account_balance = account_balance[d.account]
- d.party_balance = party_balance[(d.party_type, d.party)]
-
def update_unreconciled_amount(self):
amount = 0
cash_bank_accounts = [
@@ -1366,8 +1381,6 @@ def get_payment_entry(ref_doc, args):
"account_type": frappe.get_cached_value("Account", args.get("party_account"), "account_type"),
"account_currency": args.get("party_account_currency")
or get_account_currency(args.get("party_account")),
- "balance": get_balance_on(args.get("party_account")),
- "party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
"exchange_rate": exchange_rate,
args.get("amount_field_party"): args.get("amount"),
"is_advance": args.get("is_advance"),
@@ -1515,30 +1528,23 @@ def get_outstanding(args):
@frappe.whitelist()
-def get_party_account_and_balance(company, party_type, party, cost_center=None):
+def get_party_account_and_currency(company, party_type, party):
if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1)
account = get_party_account(party_type, party, company)
- account_balance = get_balance_on(account=account, cost_center=cost_center)
- party_balance = get_balance_on(
- party_type=party_type, party=party, company=company, cost_center=cost_center
- )
-
return {
"account": account,
- "balance": account_balance,
- "party_balance": party_balance,
"account_currency": frappe.get_cached_value("Account", account, "account_currency"),
}
@frappe.whitelist()
-def get_account_balance_and_party_type(
- account, date, company, debit=None, credit=None, exchange_rate=None, cost_center=None
+def get_account_details_and_party_type(
+ account, date, company, debit=None, credit=None, exchange_rate=None
):
- """Returns dict of account balance and party type to be set in Journal Entry on selection of account."""
+ """Returns dict of account details and party type to be set in Journal Entry on selection of account."""
if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1)
@@ -1558,7 +1564,6 @@ def get_account_balance_and_party_type(
party_type = ""
grid_values = {
- "balance": get_balance_on(account, date, cost_center=cost_center),
"party_type": party_type,
"account_type": account_details.account_type,
"account_currency": account_details.account_currency or company_currency,
diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
index a6e920b7ef6aa1988c1bdef9f4ac36bb0905254c..bf1c8a9a239aa4ee99c3615642e90b526a1e7c18 100644
--- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
@@ -166,43 +166,37 @@ class TestJournalEntry(unittest.TestCase):
jv.get("accounts")[1].credit_in_account_currency = 5000
jv.submit()
- gl_entries = frappe.db.sql(
- """select account, account_currency, debit, credit,
- debit_in_account_currency, credit_in_account_currency
- from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
- order by account asc""",
- jv.name,
- as_dict=1,
- )
+ self.voucher_no = jv.name
- self.assertTrue(gl_entries)
+ self.fields = [
+ "account",
+ "account_currency",
+ "debit",
+ "debit_in_account_currency",
+ "credit",
+ "credit_in_account_currency",
+ ]
- expected_values = {
- "_Test Bank USD - _TC": {
- "account_currency": "USD",
- "debit": 5000,
- "debit_in_account_currency": 100,
- "credit": 0,
- "credit_in_account_currency": 0,
- },
- "_Test Bank - _TC": {
+ self.expected_gle = [
+ {
+ "account": "_Test Bank - _TC",
"account_currency": "INR",
"debit": 0,
"debit_in_account_currency": 0,
"credit": 5000,
"credit_in_account_currency": 5000,
},
- }
+ {
+ "account": "_Test Bank USD - _TC",
+ "account_currency": "USD",
+ "debit": 5000,
+ "debit_in_account_currency": 100,
+ "credit": 0,
+ "credit_in_account_currency": 0,
+ },
+ ]
- for field in (
- "account_currency",
- "debit",
- "debit_in_account_currency",
- "credit",
- "credit_in_account_currency",
- ):
- for i, gle in enumerate(gl_entries):
- self.assertEqual(expected_values[gle.account][field], gle[field])
+ self.check_gl_entries()
# cancel
jv.cancel()
@@ -228,43 +222,37 @@ class TestJournalEntry(unittest.TestCase):
rjv.posting_date = nowdate()
rjv.submit()
- gl_entries = frappe.db.sql(
- """select account, account_currency, debit, credit,
- debit_in_account_currency, credit_in_account_currency
- from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
- order by account asc""",
- rjv.name,
- as_dict=1,
- )
+ self.voucher_no = rjv.name
- self.assertTrue(gl_entries)
+ self.fields = [
+ "account",
+ "account_currency",
+ "debit",
+ "credit",
+ "debit_in_account_currency",
+ "credit_in_account_currency",
+ ]
- expected_values = {
- "_Test Bank USD - _TC": {
+ self.expected_gle = [
+ {
+ "account": "_Test Bank USD - _TC",
"account_currency": "USD",
"debit": 0,
"debit_in_account_currency": 0,
"credit": 5000,
"credit_in_account_currency": 100,
},
- "Sales - _TC": {
+ {
+ "account": "Sales - _TC",
"account_currency": "INR",
"debit": 5000,
"debit_in_account_currency": 5000,
"credit": 0,
"credit_in_account_currency": 0,
},
- }
+ ]
- for field in (
- "account_currency",
- "debit",
- "debit_in_account_currency",
- "credit",
- "credit_in_account_currency",
- ):
- for i, gle in enumerate(gl_entries):
- self.assertEqual(expected_values[gle.account][field], gle[field])
+ self.check_gl_entries()
def test_disallow_change_in_account_currency_for_a_party(self):
# create jv in USD
@@ -344,23 +332,25 @@ class TestJournalEntry(unittest.TestCase):
jv.insert()
jv.submit()
- expected_values = {
- "_Test Cash - _TC": {"cost_center": cost_center},
- "_Test Bank - _TC": {"cost_center": cost_center},
- }
+ self.voucher_no = jv.name
- gl_entries = frappe.db.sql(
- """select account, cost_center, debit, credit
- from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
- order by account asc""",
- jv.name,
- as_dict=1,
- )
+ self.fields = [
+ "account",
+ "cost_center",
+ ]
- self.assertTrue(gl_entries)
+ self.expected_gle = [
+ {
+ "account": "_Test Bank - _TC",
+ "cost_center": cost_center,
+ },
+ {
+ "account": "_Test Cash - _TC",
+ "cost_center": cost_center,
+ },
+ ]
- for gle in gl_entries:
- self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
+ self.check_gl_entries()
def test_jv_with_project(self):
from erpnext.projects.doctype.project.test_project import make_project
@@ -387,23 +377,22 @@ class TestJournalEntry(unittest.TestCase):
jv.insert()
jv.submit()
- expected_values = {
- "_Test Cash - _TC": {"project": project_name},
- "_Test Bank - _TC": {"project": project_name},
- }
+ self.voucher_no = jv.name
- gl_entries = frappe.db.sql(
- """select account, project, debit, credit
- from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
- order by account asc""",
- jv.name,
- as_dict=1,
- )
+ self.fields = ["account", "project"]
- self.assertTrue(gl_entries)
+ self.expected_gle = [
+ {
+ "account": "_Test Bank - _TC",
+ "project": project_name,
+ },
+ {
+ "account": "_Test Cash - _TC",
+ "project": project_name,
+ },
+ ]
- for gle in gl_entries:
- self.assertEqual(expected_values[gle.account]["project"], gle.project)
+ self.check_gl_entries()
def test_jv_account_and_party_balance_with_cost_centre(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
@@ -426,6 +415,80 @@ class TestJournalEntry(unittest.TestCase):
account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center)
self.assertEqual(expected_account_balance, account_balance)
+ def test_repost_accounting_entries(self):
+ from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+
+ # Configure Repost Accounting Ledger for JVs
+ settings = frappe.get_doc("Repost Accounting Ledger Settings")
+ if not [x for x in settings.allowed_types if x.document_type == "Journal Entry"]:
+ settings.append("allowed_types", {"document_type": "Journal Entry", "allowed": True})
+ settings.save()
+
+ # Create JV with defaut cost center - _Test Cost Center
+ jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, save=False)
+ jv.multi_currency = 0
+ jv.submit()
+
+ # Check GL entries before reposting
+ self.voucher_no = jv.name
+
+ self.fields = [
+ "account",
+ "debit_in_account_currency",
+ "credit_in_account_currency",
+ "cost_center",
+ ]
+
+ self.expected_gle = [
+ {
+ "account": "_Test Bank - _TC",
+ "debit_in_account_currency": 0,
+ "credit_in_account_currency": 100,
+ "cost_center": "_Test Cost Center - _TC",
+ },
+ {
+ "account": "_Test Cash - _TC",
+ "debit_in_account_currency": 100,
+ "credit_in_account_currency": 0,
+ "cost_center": "_Test Cost Center - _TC",
+ },
+ ]
+
+ self.check_gl_entries()
+
+ jv.reload() # @dokos
+ # Change cost center for bank account - _Test Cost Center for BS Account
+ create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
+ jv.accounts[1].cost_center = "_Test Cost Center for BS Account - _TC"
+ jv.save()
+
+ # Check if repost flag gets set on update after submit
+ self.assertTrue(jv.repost_required)
+ jv.repost_accounting_entries()
+
+ # Check GL entries after reposting
+ jv.load_from_db()
+ self.expected_gle[0]["cost_center"] = "_Test Cost Center for BS Account - _TC"
+ self.check_gl_entries()
+
+ def check_gl_entries(self):
+ gl = frappe.qb.DocType("GL Entry")
+ query = frappe.qb.from_(gl)
+ for field in self.fields:
+ query = query.select(gl[field])
+
+ query = query.where(
+ (gl.voucher_type == "Journal Entry")
+ & (gl.voucher_no == self.voucher_no)
+ & (gl.is_cancelled == 0)
+ ).orderby(gl.account)
+
+ gl_entries = query.run(as_dict=True)
+
+ for i in range(len(self.expected_gle)):
+ for field in self.fields:
+ self.assertEqual(self.expected_gle[i][field], gl_entries[i][field])
+
def make_journal_entry(
account1,
diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
index 66425cc9a2fdd3dd8234256e3db2680aeb8953ef..2db65f48a985b1d859d3b65145cb9d36881be3ff 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
@@ -10,12 +10,10 @@
"account",
"accounting_journal",
"account_type",
- "balance",
"col_break1",
"bank_account",
"party_type",
"party",
- "party_balance",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
@@ -66,17 +64,7 @@
"print_hide": 1
},
{
- "fieldname": "balance",
- "fieldtype": "Currency",
- "label": "Account Balance",
- "no_copy": 1,
- "oldfieldname": "balance",
- "oldfieldtype": "Data",
- "options": "account_currency",
- "print_hide": 1,
- "read_only": 1
- },
- {
+ "allow_on_submit": 1,
"default": ":Company",
"description": "If Income or Expense",
"fieldname": "cost_center",
@@ -109,14 +97,6 @@
"label": "Party",
"options": "party_type"
},
- {
- "fieldname": "party_balance",
- "fieldtype": "Currency",
- "label": "Party Balance",
- "options": "account_currency",
- "print_hide": 1,
- "read_only": 1
- },
{
"fieldname": "currency_section",
"fieldtype": "Section Break",
@@ -225,6 +205,7 @@
"no_copy": 1
},
{
+ "allow_on_submit": 1,
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
@@ -304,7 +285,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2023-11-23 11:44:25.841187",
+ "modified": "2024-02-05 01:10:50.224840",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 8fea0dbe83b7252e1f172ae3d395959c781a02c5..47b979ff17391bcc6d6bc4c828c6f72d7f3154b1 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -753,6 +753,7 @@ class PurchaseInvoice(BuyingController):
"cash_bank_account",
"write_off_account",
"unrealized_profit_loss_account",
+ "is_opening",
]
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 3647d7a00da1bdcb181f5962bf9edf954962b6f6..fa77b8ffcdbf22d7006140c521195a2c22fea442 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -744,6 +744,7 @@ class SalesInvoice(SellingController):
"write_off_account",
"loyalty_redemption_account",
"unrealized_profit_loss_account",
+ "is_opening",
]
child_tables = {
"items": ("income_account", "expense_account", "discount_account"),
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 3d60e0a7719290efcea75dfecd6307c6f7f24b8c..ce18d005ebea7059df5c7bafe0524dd69c02e2a5 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -2472,27 +2472,20 @@ class AccountsController(TransactionBase):
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
+ # 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):
+ return True
- 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
+ # Check for child tables
+ for table in child_tables:
+ if check_if_child_table_updated(
+ doc_before_update.get(table), self.get(table), child_tables[table]
+ ):
+ return True
- return needs_repost
+ return False
@frappe.whitelist()
def repost_accounting_entries(self):
@@ -3624,15 +3617,12 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
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"]
+ fields_to_check = list(fields_to_check) + 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 index, item in enumerate(child_table_before_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):
+ if child_table_after_update[index].get(field) != item.get(field):
return True
return False
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 2f05ec36413155af38835804db6546cc68969cce..a3fbdda0e9e28bd6b82a021d25df2192d4257dbf 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -159,9 +159,6 @@ class StockController(AccountsController):
row.serial_no = clean_serial_no_string(row.serial_no)
def make_bundle_using_old_serial_batch_fields(self, table_name=None):
- from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
- from erpnext.stock.serial_batch_bundle import SerialBatchCreation
-
if self.get("_action") == "update_after_submit":
return
@@ -199,31 +196,21 @@ class StockController(AccountsController):
"voucher_detail_no": row.name,
"company": self.company,
"is_rejected": 1 if row.get("rejected_warehouse") else 0,
- "serial_nos": get_serial_nos(row.serial_no) if row.serial_no else None,
- "batch_no": row.batch_no,
"use_serial_batch_fields": row.use_serial_batch_fields,
"do_not_submit": True,
}
- self.update_bundle_details(bundle_details, table_name, row)
- sn_doc = SerialBatchCreation(bundle_details).make_serial_and_batch_bundle()
+ if row.qty:
+ self.update_bundle_details(bundle_details, table_name, row)
+ self.create_serial_batch_bundle(bundle_details, row)
- if sn_doc.is_rejected:
- row.rejected_serial_and_batch_bundle = sn_doc.name
- row.db_set(
- {
- "rejected_serial_and_batch_bundle": sn_doc.name,
- }
- )
- else:
- row.serial_and_batch_bundle = sn_doc.name
- row.db_set(
- {
- "serial_and_batch_bundle": sn_doc.name,
- }
- )
+ if row.get("rejected_qty"):
+ self.update_bundle_details(bundle_details, table_name, row, is_rejected=True)
+ self.create_serial_batch_bundle(bundle_details, row)
+
+ def update_bundle_details(self, bundle_details, table_name, row, is_rejected=False):
+ from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
- def update_bundle_details(self, bundle_details, table_name, row):
# Since qty field is different for different doctypes
qty = row.get("qty")
warehouse = row.get("warehouse")
@@ -242,15 +229,37 @@ class StockController(AccountsController):
qty = row.transfer_qty
warehouse = row.s_warehouse or row.t_warehouse
+ serial_nos = row.serial_no
+ if is_rejected:
+ serial_nos = row.get("rejected_serial_no")
+ type_of_transaction = "Inward" if not self.is_return else "Outward"
+ qty = row.get("rejected_qty")
+ warehouse = row.get("rejected_warehouse")
+
bundle_details.update(
{
"qty": qty,
+ "is_rejected": is_rejected,
"type_of_transaction": type_of_transaction,
"warehouse": warehouse,
"batches": frappe._dict({row.batch_no: qty}) if row.batch_no else None,
+ "serial_nos": get_serial_nos(serial_nos) if serial_nos else None,
+ "batch_no": row.batch_no,
}
)
+ def create_serial_batch_bundle(self, bundle_details, row):
+ from erpnext.stock.serial_batch_bundle import SerialBatchCreation
+
+ sn_doc = SerialBatchCreation(bundle_details).make_serial_and_batch_bundle()
+
+ field = "serial_and_batch_bundle"
+ if bundle_details.get("is_rejected"):
+ field = "rejected_serial_and_batch_bundle"
+
+ row.set(field, sn_doc.name)
+ row.db_set({field: sn_doc.name})
+
def validate_serial_nos_and_batches_with_bundle(self, row):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 15ee0ffb1d8821f1df1f319ac34d6ee5f6fec9fa..ee4fc0d908416c4192f56216954d478dc2332d97 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -355,6 +355,10 @@ erpnext.patches.v14_0.update_zero_asset_quantity_field
execute:frappe.db.set_single_value("Buying Settings", "project_update_frequency", "Each Transaction")
execute:frappe.db.set_default("date_format", frappe.db.get_single_value("System Settings", "date_format"))
erpnext.patches.v14_0.update_total_asset_cost_field
+erpnext.patches.v15_0.create_advance_payment_status
+erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes
+erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool
+
# @dokos
erpnext.patches.dokos.v3_0.add_expiration_date_to_booking_legder
@@ -383,4 +387,4 @@ erpnext.accounts.doctype.mode_of_payment.patches.migrate_fees_and_cost_center_to
# below migration patch should always run last
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20
-erpnext.patches.v14_0.set_maintain_stock_for_bom_item
+erpnext.patches.v14_0.set_maintain_stock_for_bom_item
\ No newline at end of file
diff --git a/erpnext/patches/v15_0/allow_on_submit_dimensions_for_repostable_doctypes.py b/erpnext/patches/v15_0/allow_on_submit_dimensions_for_repostable_doctypes.py
new file mode 100644
index 0000000000000000000000000000000000000000..e75610d0a537080fea9595078bf8d7254ed9fc55
--- /dev/null
+++ b/erpnext/patches/v15_0/allow_on_submit_dimensions_for_repostable_doctypes.py
@@ -0,0 +1,14 @@
+import frappe
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+)
+from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
+ get_allowed_types_from_settings,
+)
+
+
+def execute():
+ for dt in get_allowed_types_from_settings():
+ for dimension in get_accounting_dimensions():
+ frappe.db.set_value("Custom Field", dt + "-" + dimension, "allow_on_submit", 1)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
index 84b256f6c8f13c373b072bfe6e0ec134ab1755a0..271cbbc007fbd51291a49c14c679a87261fac3c2 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
@@ -1,6 +1,7 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
+
from typing import TYPE_CHECKING, Optional, overload
import frappe
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index c8f4b77d1a7884045e7a34c14d4f6922223e9e79..b88ae4c8479998cf2c865bc94893a76686839477 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -5,7 +5,7 @@
import frappe
from frappe.permissions import add_user_permission, remove_user_permission
from frappe.tests.utils import FrappeTestCase, change_settings
-from frappe.utils import add_days, add_to_date, flt, nowdate, nowtime, today
+from frappe.utils import add_days, flt, nowdate, nowtime, today
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.stock.doctype.item.test_item import (