mirror of
https://github.com/frappe/erpnext.git
synced 2026-10-29 22:57:42 +00:00
Merge pull request #62675 from rohitwaghchaure/fix-manual-adjustment-entry-message
fix: block manual selection of Adjustment Entry purpose in Stock Reconciliation
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
"naming_series",
|
||||
"company",
|
||||
"purpose",
|
||||
"is_made_from_valuation_comparison",
|
||||
"col1",
|
||||
"posting_date",
|
||||
"posting_time",
|
||||
@@ -59,6 +60,15 @@
|
||||
"options": "\nOpening Stock\nStock Reconciliation\nAdjustment Entry",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_made_from_valuation_comparison",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Made from Stock Valuation Comparison",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "col1",
|
||||
"fieldtype": "Column Break"
|
||||
@@ -193,7 +203,7 @@
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2026-09-30 12:00:00.000000",
|
||||
"modified": "2026-10-27 10:40:42.811876",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Reconciliation",
|
||||
|
||||
@@ -61,6 +61,7 @@ class StockReconciliation(StockController):
|
||||
cost_center: DF.Link | None
|
||||
difference_amount: DF.Currency
|
||||
expense_account: DF.Link | None
|
||||
is_made_from_valuation_comparison: DF.Check
|
||||
items: DF.Table[StockReconciliationItem]
|
||||
naming_series: DF.Literal["MAT-RECO-.YYYY.-"]
|
||||
posting_date: DF.Date
|
||||
@@ -117,6 +118,8 @@ class StockReconciliation(StockController):
|
||||
def validate_adjustment_entry(self):
|
||||
"""An Adjustment Entry resets each item-warehouse to its rows (erpnext.stock.valuation_adjustment),
|
||||
so none of the counting a reconciliation does applies to it."""
|
||||
AdjustmentEntry(self).validate_made_from_report()
|
||||
|
||||
if not self.expense_account:
|
||||
self.expense_account = frappe.get_cached_value(
|
||||
"Company", self.company, "stock_adjustment_account"
|
||||
|
||||
@@ -48,7 +48,11 @@ from erpnext.stock.report.stock_valuation_comparison.stock_valuation_comparison
|
||||
from erpnext.stock.report.stock_valuation_comparison.test_stock_valuation_comparison import (
|
||||
make_batch_without_batch_wise_valuation,
|
||||
)
|
||||
from erpnext.stock.valuation_adjustment import ADJUSTMENT_ENTRY, BackdatedEntryBeforeAdjustmentError
|
||||
from erpnext.stock.valuation_adjustment import (
|
||||
ADJUSTMENT_ENTRY,
|
||||
BackdatedEntryBeforeAdjustmentError,
|
||||
ManualAdjustmentEntryError,
|
||||
)
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
COMPANY = "_Test Company"
|
||||
@@ -142,6 +146,7 @@ class ValuationFixTestCase(ERPNextTestSuite):
|
||||
self.assertEqual(result["not_adjusted"], [])
|
||||
adjustment = frappe.get_doc("Stock Reconciliation", result["adjustment_entry"])
|
||||
self.assertEqual(adjustment.purpose, ADJUSTMENT_ENTRY)
|
||||
self.assertTrue(adjustment.is_made_from_valuation_comparison)
|
||||
adjustment.submit()
|
||||
|
||||
# from its date on the ledger is right
|
||||
@@ -711,6 +716,42 @@ class TestAdjustmentEntry(ValuationFixTestCase):
|
||||
adjustment.items[0].valuation_rate = 90
|
||||
self.assertRaises(frappe.ValidationError, adjustment.save)
|
||||
|
||||
def test_rows_are_checked_on_the_posting_date_set_on_save(self):
|
||||
item = self.make_fifo_issue_at_wrong_value()
|
||||
result = make_adjustment_entry(COMPANY, self.get_differences(item), get_adjustment_date(), "00:00:00")
|
||||
|
||||
adjustment = frappe.get_doc("Stock Reconciliation", result["adjustment_entry"])
|
||||
adjustment.set_posting_time = 0
|
||||
self.assertRaises(frappe.ValidationError, adjustment.save)
|
||||
|
||||
def test_adjustment_entry_cannot_be_selected_manually(self):
|
||||
item = make_item(properties={"is_stock_item": 1, "valuation_method": "FIFO"}).name
|
||||
make_stock_entry(item_code=item, target=WAREHOUSE, qty=10, rate=100)
|
||||
reconciliation = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Stock Reconciliation",
|
||||
"company": COMPANY,
|
||||
"purpose": ADJUSTMENT_ENTRY,
|
||||
"items": [{"item_code": item, "warehouse": WAREHOUSE, "qty": 10, "valuation_rate": 100}],
|
||||
}
|
||||
)
|
||||
self.assertRaises(ManualAdjustmentEntryError, reconciliation.insert)
|
||||
|
||||
reconciliation.purpose = "Stock Reconciliation"
|
||||
reconciliation.items[0].qty = 12
|
||||
reconciliation.insert()
|
||||
reconciliation.purpose = ADJUSTMENT_ENTRY
|
||||
self.assertRaises(ManualAdjustmentEntryError, reconciliation.save)
|
||||
|
||||
def test_duplicate_of_adjustment_entry_is_not_allowed(self):
|
||||
item = self.make_fifo_issue_at_wrong_value()
|
||||
result = make_adjustment_entry(COMPANY, self.get_differences(item), get_adjustment_date(), "00:00:00")
|
||||
|
||||
copy = frappe.copy_doc(
|
||||
frappe.get_doc("Stock Reconciliation", result["adjustment_entry"]), ignore_no_copy=False
|
||||
)
|
||||
self.assertRaises(ManualAdjustmentEntryError, copy.insert)
|
||||
|
||||
def test_no_backdated_entry_before_an_adjustment_entry(self):
|
||||
item = self.make_fifo_issue_at_wrong_value()
|
||||
issue = frappe.get_last_doc("Stock Entry", filters={"posting_date": last_year(50)})
|
||||
|
||||
@@ -51,6 +51,10 @@ class BackdatedEntryBeforeAdjustmentError(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class ManualAdjustmentEntryError(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class AdjustmentRow:
|
||||
"""One row of an Adjustment Entry: stock counted in at a rate, as a batch, serial nos, or
|
||||
@@ -317,6 +321,7 @@ def make_adjustment_entry(
|
||||
doc.update(
|
||||
{
|
||||
"purpose": ADJUSTMENT_ENTRY,
|
||||
"is_made_from_valuation_comparison": 1,
|
||||
"company": company,
|
||||
"posting_date": posting_date,
|
||||
"posting_time": posting_time,
|
||||
@@ -429,6 +434,18 @@ class AdjustmentEntry:
|
||||
sum(flt(row.amount_difference) for row in self.doc.items), self.doc.precision("difference_amount")
|
||||
)
|
||||
|
||||
def validate_made_from_report(self) -> None:
|
||||
if self.doc.is_made_from_valuation_comparison:
|
||||
return
|
||||
|
||||
frappe.throw(
|
||||
_(
|
||||
"Adjustment Entry can only be used by the system from the Stock Valuation Comparison report. Users can only select the purpose Opening Stock to add opening stock, or Stock Reconciliation to update stock quantity or valuation rate."
|
||||
),
|
||||
title=_("Not Allowed Purpose"),
|
||||
exc=ManualAdjustmentEntryError,
|
||||
)
|
||||
|
||||
def set_row_amounts(self, plan: AdjustmentPlan, rows) -> None:
|
||||
"""The first row of an item-warehouse shows what the ledger held, which the reset counts out."""
|
||||
for index, row in enumerate(rows):
|
||||
|
||||
Reference in New Issue
Block a user