mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-14 18:58:49 +00:00
* fix: for purchases do voucher based reposting (#56601)
(cherry picked from commit 5523c15ab8)
* chore: fix type hints
---------
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
This commit is contained in:
@@ -174,14 +174,20 @@ def get_columns(filters):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_reposting_entries(rows, company):
|
||||
def create_reposting_entries(rows: str | list, company: str):
|
||||
if isinstance(rows, str):
|
||||
rows = parse_json(rows)
|
||||
|
||||
entries = []
|
||||
|
||||
item_wh = frappe._dict()
|
||||
vouchers = [row.get("voucher_no") for row in rows]
|
||||
vouchers = [
|
||||
row.get("voucher_no")
|
||||
for row in rows
|
||||
if row.get("voucher_type") not in ["Purchase Receipt", "Purchase Invoice"]
|
||||
]
|
||||
repost_based_on_transaction(rows, company, entries)
|
||||
|
||||
sles = get_stock_ledgers(vouchers)
|
||||
for sle in sles:
|
||||
key = (sle.item_code, sle.warehouse)
|
||||
@@ -214,3 +220,39 @@ def create_reposting_entries(rows, company):
|
||||
if entries:
|
||||
entries = ", ".join(entries)
|
||||
frappe.msgprint(_("Reposting entries created: {0}").format(entries))
|
||||
|
||||
|
||||
def repost_based_on_transaction(rows, company=None, entries=None):
|
||||
if entries is None:
|
||||
entries = []
|
||||
|
||||
duplicate_vouchers = set()
|
||||
for row in rows:
|
||||
if (
|
||||
row.get("voucher_type") == "Purchase Invoice"
|
||||
and frappe.get_cached_value("Purchase Invoice", row.get("voucher_no"), "update_stock") == 0
|
||||
):
|
||||
continue
|
||||
|
||||
if row.get("voucher_type") in ["Purchase Receipt", "Purchase Invoice"]:
|
||||
voucher_key = (row.get("voucher_type"), row.get("voucher_no"))
|
||||
if voucher_key in duplicate_vouchers:
|
||||
continue
|
||||
|
||||
duplicate_vouchers.add(voucher_key)
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Repost Item Valuation",
|
||||
"based_on": "Transaction",
|
||||
"status": "Queued",
|
||||
"voucher_type": row.get("voucher_type"),
|
||||
"voucher_no": row.get("voucher_no"),
|
||||
"posting_date": row.get("posting_date"),
|
||||
"posting_time": row.get("posting_time"),
|
||||
"company": company,
|
||||
"allow_nagative_stock": 1,
|
||||
"recalculate_valuation_rate": 1,
|
||||
}
|
||||
).submit()
|
||||
|
||||
entries.append(get_link_to_form("Repost Item Valuation", doc.name))
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.utils import today
|
||||
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
from erpnext.stock.report.stock_and_account_value_comparison.stock_and_account_value_comparison import (
|
||||
create_reposting_entries,
|
||||
execute,
|
||||
)
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
PI_COMPANY = "_Test Company with perpetual inventory"
|
||||
PI_STORES = "Stores - TCP1"
|
||||
|
||||
|
||||
class TestStockAndAccountValueComparison(ERPNextTestSuite):
|
||||
def test_purchase_voucher_reposted_transaction_based(self):
|
||||
# A Purchase Receipt whose GL entries are missing must surface in the report and, when reposted
|
||||
# from it, be reposted Transaction-based (so its own GL is regenerated) rather than the slower
|
||||
# Item-and-Warehouse based reposting.
|
||||
item = make_item(properties={"is_stock_item": 1, "valuation_method": "FIFO"}).name
|
||||
|
||||
pr = make_purchase_receipt(item_code=item, company=PI_COMPANY, warehouse=PI_STORES, qty=5, rate=100)
|
||||
|
||||
# Simulate the out-of-sync state: stock ledger exists but the accounting ledger does not.
|
||||
frappe.db.delete("GL Entry", {"voucher_type": "Purchase Receipt", "voucher_no": pr.name})
|
||||
|
||||
# The receipt now shows up in the comparison report (stock value 500 vs account value 0).
|
||||
filters = frappe._dict(company=PI_COMPANY, as_on_date=today())
|
||||
_columns, data = execute(filters)
|
||||
|
||||
row = next((d for d in data if d.get("voucher_no") == pr.name), None)
|
||||
self.assertIsNotNone(row, "Out-of-sync Purchase Receipt should appear in the report")
|
||||
self.assertEqual(row.get("voucher_type"), "Purchase Receipt")
|
||||
|
||||
# Repost from the report.
|
||||
create_reposting_entries([row], PI_COMPANY)
|
||||
|
||||
# A Transaction-based Repost Item Valuation must have been created for this voucher...
|
||||
transaction_rivs = frappe.get_all(
|
||||
"Repost Item Valuation",
|
||||
filters={"voucher_no": pr.name, "voucher_type": "Purchase Receipt"},
|
||||
fields=["name", "based_on"],
|
||||
)
|
||||
|
||||
self.assertTrue(transaction_rivs, "Expected a Repost Item Valuation for the Purchase Receipt")
|
||||
self.assertTrue(all(riv.based_on == "Transaction" for riv in transaction_rivs))
|
||||
|
||||
# ...and no Item-and-Warehouse based reposting should have been created for this item.
|
||||
item_wh_rivs = frappe.get_all(
|
||||
"Repost Item Valuation",
|
||||
filters={"based_on": "Item and Warehouse", "item_code": item},
|
||||
)
|
||||
self.assertFalse(item_wh_rivs, "Purchase vouchers must not be reposted Item-and-Warehouse based")
|
||||
Reference in New Issue
Block a user