mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-02 21:18:27 +00:00
Merge pull request #50660 from rohitwaghchaure/fixed-repost-gl-only
feat: repost GL Entries only
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
"label": "Home",
|
||||
"link_to": "Home",
|
||||
"link_type": "Workspace",
|
||||
"modified": "2025-11-18 12:06:56.506311",
|
||||
"modified": "2025-11-20 16:09:28.269913",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Home",
|
||||
"owner": "Administrator",
|
||||
|
||||
@@ -4536,6 +4536,60 @@ class TestPurchaseReceipt(IntegrationTestCase):
|
||||
if row.account == expense_contra_account:
|
||||
self.assertEqual(row.credit, 1000)
|
||||
|
||||
def test_repost_gl_entries(self):
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||
|
||||
item = "Test Item for Repost GL Entries"
|
||||
make_item(item, {"is_stock_item": 1})
|
||||
company = "_Test Company with perpetual inventory"
|
||||
|
||||
account = "Reposting Adjustment - TCP1"
|
||||
if not frappe.db.exists("Account", account):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Account",
|
||||
"account_name": "Reposting Adjustment",
|
||||
"parent_account": "Stock Expenses - TCP1",
|
||||
"company": company,
|
||||
"is_group": 0,
|
||||
"account_type": "Expense Account",
|
||||
}
|
||||
).insert()
|
||||
|
||||
se = make_stock_entry(
|
||||
item_code=item,
|
||||
qty=10,
|
||||
rate=100,
|
||||
company=company,
|
||||
target="Stores - TCP1",
|
||||
)
|
||||
|
||||
gl_entries = get_gl_entries(se.doctype, se.name)
|
||||
for row in gl_entries:
|
||||
self.assertTrue(row.account in ["Stock In Hand - TCP1", "Stock Adjustment - TCP1"])
|
||||
|
||||
se.items[0].db_set("expense_account", account)
|
||||
se.reload()
|
||||
|
||||
repost_doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Repost Item Valuation",
|
||||
"based_on": "Transaction",
|
||||
"voucher_type": se.doctype,
|
||||
"voucher_no": se.name,
|
||||
"posting_date": se.posting_date,
|
||||
"posting_time": se.posting_time,
|
||||
"company": se.company,
|
||||
"repost_only_accounting_ledgers": 1,
|
||||
}
|
||||
)
|
||||
|
||||
repost_doc.submit()
|
||||
|
||||
gl_entries = get_gl_entries(se.doctype, se.name)
|
||||
for row in gl_entries:
|
||||
self.assertTrue(row.account in ["Stock In Hand - TCP1", account])
|
||||
|
||||
|
||||
def prepare_data_for_internal_transfer():
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
|
||||
|
||||
@@ -41,7 +41,9 @@ frappe.ui.form.on("Repost Item Valuation", {
|
||||
});
|
||||
}
|
||||
|
||||
frm.trigger("setup_realtime_progress");
|
||||
if (frm.doc.status !== "Completed") {
|
||||
frm.trigger("setup_realtime_progress");
|
||||
}
|
||||
},
|
||||
|
||||
based_on: function (frm) {
|
||||
@@ -84,7 +86,9 @@ frappe.ui.form.on("Repost Item Valuation", {
|
||||
}).addClass("btn-primary");
|
||||
}
|
||||
|
||||
frm.trigger("show_reposting_progress");
|
||||
if (frm.doc.status !== "Completed") {
|
||||
frm.trigger("show_reposting_progress");
|
||||
}
|
||||
|
||||
if (frm.doc.status === "Queued" && frm.doc.docstatus === 1) {
|
||||
frm.trigger("execute_reposting");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "hash",
|
||||
"creation": "2022-01-11 15:03:38.273179",
|
||||
"doctype": "DocType",
|
||||
@@ -16,6 +17,8 @@
|
||||
"column_break_5",
|
||||
"status",
|
||||
"company",
|
||||
"reposting_reference",
|
||||
"repost_only_accounting_ledgers",
|
||||
"allow_negative_stock",
|
||||
"via_landed_cost_voucher",
|
||||
"allow_zero_rate",
|
||||
@@ -228,13 +231,28 @@
|
||||
"fieldname": "recreate_stock_ledgers",
|
||||
"fieldtype": "Check",
|
||||
"label": "Recreate Stock Ledgers"
|
||||
},
|
||||
{
|
||||
"depends_on": "repost_only_accounting_ledgers",
|
||||
"fieldname": "reposting_reference",
|
||||
"fieldtype": "Data",
|
||||
"label": "Reposting Reference",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.based_on === \"Transaction\"",
|
||||
"description": "Stock Ledgers won\u2019t be reposted.",
|
||||
"fieldname": "repost_only_accounting_ledgers",
|
||||
"fieldtype": "Check",
|
||||
"label": "Repost Only Accounting Ledgers"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-03-31 12:38:20.566196",
|
||||
"modified": "2025-11-20 18:24:48.808526",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Repost Item Valuation",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.desk.form.load import get_attachments
|
||||
@@ -48,7 +50,9 @@ class RepostItemValuation(Document):
|
||||
posting_date: DF.Date
|
||||
posting_time: DF.Time | None
|
||||
recreate_stock_ledgers: DF.Check
|
||||
repost_only_accounting_ledgers: DF.Check
|
||||
reposting_data_file: DF.Attach | None
|
||||
reposting_reference: DF.Data | None
|
||||
status: DF.Literal["Queued", "In Progress", "Completed", "Skipped", "Failed"]
|
||||
total_reposting_count: DF.Int
|
||||
via_landed_cost_voucher: DF.Check
|
||||
@@ -70,6 +74,7 @@ class RepostItemValuation(Document):
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
self.reset_repost_only_accounting_ledgers()
|
||||
self.set_company()
|
||||
self.validate_period_closing_voucher()
|
||||
self.set_status(write=False)
|
||||
@@ -78,6 +83,10 @@ class RepostItemValuation(Document):
|
||||
self.reset_recreate_stock_ledgers()
|
||||
self.validate_recreate_stock_ledgers()
|
||||
|
||||
def reset_repost_only_accounting_ledgers(self):
|
||||
if self.repost_only_accounting_ledgers and self.based_on != "Transaction":
|
||||
self.repost_only_accounting_ledgers = 0
|
||||
|
||||
def validate_recreate_stock_ledgers(self):
|
||||
if not self.recreate_stock_ledgers:
|
||||
return
|
||||
@@ -242,11 +251,39 @@ class RepostItemValuation(Document):
|
||||
self.distinct_item_and_warehouse = None
|
||||
self.items_to_be_repost = None
|
||||
self.gl_reposting_index = 0
|
||||
self.total_reposting_count = 0
|
||||
self.clear_attachment()
|
||||
self.db_update()
|
||||
|
||||
def skipped_similar_reposts(self):
|
||||
repost_entries = frappe.get_all(
|
||||
"Repost Item Valuation",
|
||||
filters={
|
||||
"based_on": "Transaction",
|
||||
"voucher_type": self.voucher_type,
|
||||
"voucher_no": self.voucher_no,
|
||||
"docstatus": 1,
|
||||
"repost_only_accounting_ledgers": 1,
|
||||
"status": "Queued",
|
||||
"reposting_reference": ("is", "set"),
|
||||
},
|
||||
fields=["name", "reposting_reference"],
|
||||
)
|
||||
|
||||
for entry in repost_entries:
|
||||
if (
|
||||
frappe.db.get_value("Repost Item Valuation", entry.reposting_reference, "status")
|
||||
== "Completed"
|
||||
):
|
||||
frappe.db.set_value("Repost Item Valuation", entry.name, "status", "Skipped")
|
||||
|
||||
def deduplicate_similar_repost(self):
|
||||
"""Deduplicate similar reposts based on item-warehouse-posting combination."""
|
||||
|
||||
if self.repost_only_accounting_ledgers:
|
||||
self.skipped_similar_reposts()
|
||||
return
|
||||
|
||||
if self.based_on != "Item and Warehouse":
|
||||
return
|
||||
|
||||
@@ -284,6 +321,19 @@ class RepostItemValuation(Document):
|
||||
doc.update_stock_ledger(allow_negative_stock=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def bulk_restart_reposting(names):
|
||||
names = json.loads(names)
|
||||
for name in names:
|
||||
doc = frappe.get_doc("Repost Item Valuation", name)
|
||||
if doc.status != "Failed":
|
||||
continue
|
||||
|
||||
doc.restart_reposting()
|
||||
|
||||
frappe.msgprint(_("Repost Item Valuation restarted for selected failed records."))
|
||||
|
||||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("Repost Item Valuation", ["warehouse", "item_code"], "item_warehouse")
|
||||
|
||||
@@ -304,7 +354,9 @@ def repost(doc):
|
||||
if doc.recreate_stock_ledgers:
|
||||
doc.recreate_stock_ledger_entries()
|
||||
|
||||
repost_sl_entries(doc)
|
||||
if not doc.repost_only_accounting_ledgers:
|
||||
repost_sl_entries(doc)
|
||||
|
||||
repost_gl_entries(doc)
|
||||
|
||||
doc.set_status("Completed")
|
||||
@@ -393,15 +445,34 @@ def repost_gl_entries(doc):
|
||||
if not cint(erpnext.is_perpetual_inventory_enabled(doc.company)):
|
||||
return
|
||||
|
||||
if doc.repost_only_accounting_ledgers and doc.based_on == "Transaction":
|
||||
transactions = [(doc.voucher_type, doc.voucher_no)]
|
||||
repost_gle_for_stock_vouchers(
|
||||
transactions,
|
||||
doc.posting_date,
|
||||
doc.company,
|
||||
repost_doc=doc,
|
||||
)
|
||||
return
|
||||
|
||||
# directly modified transactions
|
||||
directly_dependent_transactions = _get_directly_dependent_vouchers(doc)
|
||||
repost_affected_transaction = get_affected_transactions(doc)
|
||||
repost_gle_for_stock_vouchers(
|
||||
directly_dependent_transactions + list(repost_affected_transaction),
|
||||
doc.posting_date,
|
||||
doc.company,
|
||||
repost_doc=doc,
|
||||
)
|
||||
|
||||
transactions = directly_dependent_transactions + list(repost_affected_transaction)
|
||||
if doc.based_on == "Item and Warehouse" and not doc.repost_only_accounting_ledgers:
|
||||
make_reposting_for_accounting_ledgers(
|
||||
transactions,
|
||||
doc.company,
|
||||
repost_doc=doc,
|
||||
)
|
||||
else:
|
||||
repost_gle_for_stock_vouchers(
|
||||
transactions,
|
||||
doc.posting_date,
|
||||
doc.company,
|
||||
repost_doc=doc,
|
||||
)
|
||||
|
||||
|
||||
def _get_directly_dependent_vouchers(doc):
|
||||
@@ -477,14 +548,17 @@ def repost_entries():
|
||||
|
||||
for row in riv_entries:
|
||||
doc = frappe.get_doc("Repost Item Valuation", row.name)
|
||||
if (
|
||||
doc.repost_only_accounting_ledgers
|
||||
and doc.reposting_reference
|
||||
and frappe.db.get_value("Repost Item Valuation", doc.reposting_reference, "status") != "Completed"
|
||||
):
|
||||
continue
|
||||
|
||||
if doc.status in ("Queued", "In Progress"):
|
||||
repost(doc)
|
||||
doc.deduplicate_similar_repost()
|
||||
|
||||
riv_entries = get_repost_item_valuation_entries()
|
||||
if riv_entries:
|
||||
return
|
||||
|
||||
|
||||
def get_repost_item_valuation_entries():
|
||||
return frappe.db.sql(
|
||||
@@ -529,3 +603,28 @@ def execute_repost_item_valuation():
|
||||
"name",
|
||||
):
|
||||
frappe.get_doc("Scheduled Job Type", name).enqueue(force=True)
|
||||
|
||||
|
||||
def make_reposting_for_accounting_ledgers(transactions, company, repost_doc):
|
||||
for voucher_type, voucher_no in transactions:
|
||||
if frappe.db.exists(
|
||||
"Repost Item Valuation",
|
||||
{
|
||||
"voucher_type": voucher_type,
|
||||
"voucher_no": voucher_no,
|
||||
"docstatus": 1,
|
||||
"reposting_reference": repost_doc.name,
|
||||
"repost_only_accounting_ledgers": 1,
|
||||
"status": "Queued",
|
||||
},
|
||||
):
|
||||
continue
|
||||
|
||||
new_repost_doc = frappe.new_doc("Repost Item Valuation")
|
||||
new_repost_doc.company = company
|
||||
new_repost_doc.voucher_type = voucher_type
|
||||
new_repost_doc.voucher_no = voucher_no
|
||||
new_repost_doc.repost_only_accounting_ledgers = 1
|
||||
new_repost_doc.reposting_reference = repost_doc.name
|
||||
new_repost_doc.flags.ignore_permissions = True
|
||||
new_repost_doc.submit()
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
frappe.listview_settings["Repost Item Valuation"] = {
|
||||
add_fields: ["status", "name", "voucher_type", "voucher_no"],
|
||||
get_indicator: function (doc) {
|
||||
if (doc.status === "Completed") {
|
||||
// Closed
|
||||
return [__("Completed"), "green", "status,=,Completed"];
|
||||
} else if (doc.status === "Queued") {
|
||||
// on hold
|
||||
return [__("Queued"), "red", "status,=,Queued"];
|
||||
} else if (doc.status === "In Progress") {
|
||||
// on hold
|
||||
return [__("In Progress"), "orange", "status,=,In Progress"];
|
||||
} else if (doc.status === "Failed") {
|
||||
return [__("Failed"), "red", "status,=,Failed"];
|
||||
} else {
|
||||
return [__(doc.status), "blue", true];
|
||||
}
|
||||
},
|
||||
onload: function (listview) {
|
||||
var method =
|
||||
"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.bulk_restart_reposting";
|
||||
|
||||
listview.page.add_action_item(__("Restart Failed Entries"), () => {
|
||||
listview.call_for_selected_items(method, { status: "Failed" });
|
||||
});
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user