mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-15 15:38:39 +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()
|
@frappe.whitelist()
|
||||||
def create_reposting_entries(rows, company):
|
def create_reposting_entries(rows: str | list, company: str):
|
||||||
if isinstance(rows, str):
|
if isinstance(rows, str):
|
||||||
rows = parse_json(rows)
|
rows = parse_json(rows)
|
||||||
|
|
||||||
entries = []
|
entries = []
|
||||||
|
|
||||||
item_wh = frappe._dict()
|
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)
|
sles = get_stock_ledgers(vouchers)
|
||||||
for sle in sles:
|
for sle in sles:
|
||||||
key = (sle.item_code, sle.warehouse)
|
key = (sle.item_code, sle.warehouse)
|
||||||
@@ -214,3 +220,39 @@ def create_reposting_entries(rows, company):
|
|||||||
if entries:
|
if entries:
|
||||||
entries = ", ".join(entries)
|
entries = ", ".join(entries)
|
||||||
frappe.msgprint(_("Reposting entries created: {0}").format(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