fix(stock): compare transit quantities in stock UOM (backport #59202) (#59243)

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
This commit is contained in:
mergify[bot]
2026-09-22 08:50:17 +00:00
committed by GitHub
parent 9fe04988c2
commit 7d5293e83e
2 changed files with 86 additions and 2 deletions

View File

@@ -3697,6 +3697,12 @@ def _qty_tolerance(precision: int) -> float:
@frappe.whitelist()
def make_stock_in_entry(source_name, target_doc=None):
qty_precision = frappe.get_precision("Stock Entry Detail", "transfer_qty")
def get_remaining_transfer_qty(source_doc):
remaining_qty = flt(source_doc.transfer_qty) - flt(source_doc.transferred_qty)
return flt(remaining_qty, qty_precision)
def set_missing_values(source, target):
target.stock_entry_type = "Material Transfer"
target.set_missing_values()
@@ -3716,7 +3722,7 @@ def make_stock_in_entry(source_name, target_doc=None):
target_doc.t_warehouse = warehouse
target_doc.s_warehouse = source_doc.t_warehouse
target_doc.qty = source_doc.qty - source_doc.transferred_qty
target_doc.qty = get_remaining_transfer_qty(source_doc) / flt(source_doc.conversion_factor)
doclist = get_mapped_doc(
"Stock Entry",
@@ -3736,7 +3742,7 @@ def make_stock_in_entry(source_name, target_doc=None):
"batch_no": "batch_no",
},
"postprocess": update_item,
"condition": lambda doc: flt(doc.qty) - flt(doc.transferred_qty) > 0.00001,
"condition": lambda doc: get_remaining_transfer_qty(doc) > 0,
},
},
target_doc,

View File

@@ -293,6 +293,84 @@ class TestStockEntry(FrappeTestCase):
)
self.assertRaises(frappe.ValidationError, se.save)
def test_end_transit_qty_with_uom_conversion(self):
"""transferred_qty is tracked in the stock UOM, so the end transit qty must be converted back."""
company = "_Test Company"
source_warehouse = "_Test Warehouse - _TC"
target_warehouse = "_Test Warehouse 1 - _TC"
transit_warehouse = get_in_transit_warehouse(company)
item_code = make_item(
"_Test Transit UOM Conversion Item",
{"is_stock_item": 1, "stock_uom": "Nos", "uoms": [{"uom": "Kg", "conversion_factor": 0.5}]},
).name
make_stock_entry(item_code=item_code, target=source_warehouse, qty=100, basic_rate=100)
transit_entry = make_stock_entry(
item_code=item_code,
source=source_warehouse,
target=transit_warehouse,
purpose="Material Transfer",
add_to_transit=1,
qty=10,
basic_rate=100,
do_not_save=True,
)
transit_entry.items[0].uom = "Kg"
transit_entry.items[0].conversion_factor = 0.5
transit_entry.save().submit()
self.assertEqual(transit_entry.items[0].transfer_qty, 5)
partial_entry = make_stock_in_entry(transit_entry.name)
partial_entry.to_warehouse = target_warehouse
partial_entry.items[0].qty = 4
partial_entry.items[0].t_warehouse = target_warehouse
partial_entry.save().submit()
remaining_entry = make_stock_in_entry(transit_entry.name)
self.assertEqual(remaining_entry.items[0].uom, "Kg")
self.assertEqual(remaining_entry.items[0].qty, 6)
remaining_entry.to_warehouse = target_warehouse
remaining_entry.items[0].t_warehouse = target_warehouse
remaining_entry.save().submit()
self.assertFalse(make_stock_in_entry(transit_entry.name).get("items"))
def test_end_transit_maps_smallest_remaining_qty(self):
"""The smallest storable remainder survives binary subtraction, 2.001 - 2 is 0.0009999999999998899."""
company = "_Test Company"
source_warehouse = "_Test Warehouse - _TC"
target_warehouse = "_Test Warehouse 1 - _TC"
transit_warehouse = get_in_transit_warehouse(company)
item_code = make_item(
"_Test Transit Fractional Item", {"is_stock_item": 1, "stock_uom": "Litre"}
).name
smallest_qty = 1 / (10 ** frappe.get_precision("Stock Entry Detail", "transfer_qty"))
make_stock_entry(item_code=item_code, target=source_warehouse, qty=100, basic_rate=100)
transit_entry = make_stock_entry(
item_code=item_code,
source=source_warehouse,
target=transit_warehouse,
purpose="Material Transfer",
add_to_transit=1,
qty=2 + smallest_qty,
basic_rate=100,
)
partial_entry = make_stock_in_entry(transit_entry.name)
partial_entry.to_warehouse = target_warehouse
partial_entry.items[0].qty = 2
partial_entry.items[0].t_warehouse = target_warehouse
partial_entry.save().submit()
remaining_entry = make_stock_in_entry(transit_entry.name)
self.assertEqual(remaining_entry.items[0].qty, smallest_qty)
def test_material_receipt_gl_entry(self):
company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")