diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 4f22d83244e..b7aa97ddb54 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -4077,6 +4077,54 @@ class TestPurchaseReceipt(FrappeTestCase): pr.reload() self.assertEqual(pr.status, "To Bill") + def test_recreate_stock_ledgers(self): + item_code = "Test Item for Recreate Stock Ledgers" + create_item(item_code) + + pr = make_purchase_receipt(item_code=item_code, qty=10, rate=100) + pr.submit() + + sles = frappe.get_all( + "Stock Ledger Entry", + filters={"voucher_type": pr.doctype, "voucher_no": pr.name}, + pluck="name", + ) + + self.assertTrue(sles) + + for row in sles: + doc = frappe.get_doc("Stock Ledger Entry", row) + doc.delete() + + sles = frappe.get_all( + "Stock Ledger Entry", + filters={"voucher_type": pr.doctype, "voucher_no": pr.name}, + pluck="name", + ) + + self.assertFalse(sles) + + frappe.get_doc( + { + "doctype": "Repost Item Valuation", + "based_on": "Transaction", + "voucher_type": pr.doctype, + "voucher_no": pr.name, + "posting_date": pr.posting_date, + "posting_time": pr.posting_time, + "company": pr.company, + "recreate_stock_ledgers": 1, + } + ).submit() + + sles = frappe.get_all( + "Stock Ledger Entry", + filters={"voucher_type": pr.doctype, "voucher_no": pr.name}, + pluck="name", + ) + + self.assertTrue(sles) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json index 67e97964e18..bd70072e4bd 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json @@ -19,6 +19,7 @@ "allow_negative_stock", "via_landed_cost_voucher", "allow_zero_rate", + "recreate_stock_ledgers", "amended_from", "error_section", "error_log", @@ -220,12 +221,20 @@ "label": "Reposting Data File", "no_copy": 1, "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.based_on == \"Transaction\"", + "fieldname": "recreate_stock_ledgers", + "fieldtype": "Check", + "label": "Recreate Stock Ledgers" } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-06-27 16:55:23.150146", + "modified": "2025-03-31 12:38:20.566196", "modified_by": "Administrator", "module": "Stock", "name": "Repost Item Valuation", @@ -274,7 +283,7 @@ "write": 1 } ], - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [] } diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index 9026a9a9a8b..880acb65dc3 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -47,6 +47,7 @@ class RepostItemValuation(Document): items_to_be_repost: DF.Code | None posting_date: DF.Date posting_time: DF.Time | None + recreate_stock_ledgers: DF.Check reposting_data_file: DF.Attach | None status: DF.Literal["Queued", "In Progress", "Completed", "Skipped", "Failed"] total_reposting_count: DF.Int @@ -74,6 +75,7 @@ class RepostItemValuation(Document): self.reset_field_values() self.set_company() self.validate_accounts_freeze() + self.reset_recreate_stock_ledgers() def validate_period_closing_voucher(self): # Period Closing Voucher @@ -105,6 +107,10 @@ class RepostItemValuation(Document): msg = f"Due to closing stock balance {name}, you cannot repost item valuation before {to_date}" frappe.throw(_(msg)) + def reset_recreate_stock_ledgers(self): + if self.recreate_stock_ledgers and self.based_on != "Transaction": + self.recreate_stock_ledgers = 0 + def get_closing_stock_balance(self): filters = { "company": self.company, @@ -250,6 +256,16 @@ class RepostItemValuation(Document): filters, ) + def recreate_stock_ledger_entries(self): + """Recreate Stock Ledger Entries for the transaction.""" + if self.based_on == "Transaction" and self.recreate_stock_ledgers: + doc = frappe.get_doc(self.voucher_type, self.voucher_no) + doc.docstatus = 2 + doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True) + + doc.docstatus = 1 + doc.update_stock_ledger(allow_negative_stock=True) + def on_doctype_update(): frappe.db.add_index("Repost Item Valuation", ["warehouse", "item_code"], "item_warehouse") @@ -268,6 +284,9 @@ def repost(doc): if not frappe.flags.in_test: frappe.db.commit() + if doc.recreate_stock_ledgers: + doc.recreate_stock_ledger_entries() + repost_sl_entries(doc) repost_gl_entries(doc) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 4627c8aecfa..9776486eabc 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -184,11 +184,16 @@ def validate_serial_no(sle): frappe.throw(_(msg), title=_(title), exc=SerialNoExistsInFutureTransaction) -def validate_cancellation(args): - if args[0].get("is_cancelled"): +def validate_cancellation(kargs): + if kargs[0].get("is_cancelled"): repost_entry = frappe.db.get_value( "Repost Item Valuation", - {"voucher_type": args[0].voucher_type, "voucher_no": args[0].voucher_no, "docstatus": 1}, + { + "voucher_type": kargs[0].voucher_type, + "voucher_no": kargs[0].voucher_no, + "docstatus": 1, + "recreate_stock_ledgers": 0, + }, ["name", "status"], as_dict=1, )