mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-29 03:28:32 +00:00
Merge pull request #51166 from mihir-kandoi/gh50060
This commit is contained in:
@@ -393,12 +393,16 @@ class StatusUpdater(Document):
|
||||
self.item_allowance,
|
||||
self.global_qty_allowance,
|
||||
self.global_amount_allowance,
|
||||
) = get_allowance_for(
|
||||
item["item_code"],
|
||||
self.item_allowance,
|
||||
self.global_qty_allowance,
|
||||
self.global_amount_allowance,
|
||||
qty_or_amount,
|
||||
) = (
|
||||
get_allowance_for(
|
||||
item["item_code"],
|
||||
self.item_allowance,
|
||||
self.global_qty_allowance,
|
||||
self.global_amount_allowance,
|
||||
qty_or_amount,
|
||||
)
|
||||
if args["source_dt"] != "Pick List Item"
|
||||
else (0, {}, None, None)
|
||||
)
|
||||
|
||||
role_allowed_to_over_deliver_receive = frappe.get_single_value(
|
||||
@@ -436,14 +440,17 @@ class StatusUpdater(Document):
|
||||
):
|
||||
return
|
||||
|
||||
if qty_or_amount == "qty":
|
||||
action_msg = _(
|
||||
'To allow over receipt / delivery, update "Over Receipt/Delivery Allowance" in Stock Settings or the Item.'
|
||||
)
|
||||
if args["source_dt"] != "Pick List Item":
|
||||
if qty_or_amount == "qty":
|
||||
action_msg = _(
|
||||
'To allow over receipt / delivery, update "Over Receipt/Delivery Allowance" in Stock Settings or the Item.'
|
||||
)
|
||||
else:
|
||||
action_msg = _(
|
||||
'To allow over billing, update "Over Billing Allowance" in Accounts Settings or the Item.'
|
||||
)
|
||||
else:
|
||||
action_msg = _(
|
||||
'To allow over billing, update "Over Billing Allowance" in Accounts Settings or the Item.'
|
||||
)
|
||||
action_msg = None
|
||||
|
||||
frappe.throw(
|
||||
_(
|
||||
@@ -455,8 +462,7 @@ class StatusUpdater(Document):
|
||||
frappe.bold(_(self.doctype)),
|
||||
frappe.bold(item.get("item_code")),
|
||||
)
|
||||
+ "<br><br>"
|
||||
+ action_msg,
|
||||
+ ("<br><br>" + action_msg if action_msg else ""),
|
||||
OverAllowanceError,
|
||||
title=_("Limit Crossed"),
|
||||
)
|
||||
|
||||
@@ -451,4 +451,5 @@ erpnext.patches.v15_0.migrate_old_item_wise_tax_detail_data_to_table
|
||||
erpnext.patches.v16_0.migrate_budget_records_to_new_structure
|
||||
erpnext.patches.v16_0.update_currency_exchange_settings_for_frankfurter #2025-12-11
|
||||
erpnext.patches.v16_0.migrate_account_freezing_settings_to_company
|
||||
erpnext.patches.v16_0.populate_budget_distribution_total
|
||||
erpnext.patches.v16_0.populate_budget_distribution_total
|
||||
erpnext.patches.v16_0.set_mr_picked_qty
|
||||
12
erpnext/patches/v16_0/set_mr_picked_qty.py
Normal file
12
erpnext/patches/v16_0/set_mr_picked_qty.py
Normal file
@@ -0,0 +1,12 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
if data := frappe.get_all(
|
||||
"Pick List Item",
|
||||
filters={"material_request_item": ["is", "set"], "docstatus": 1},
|
||||
fields=["material_request_item", {"SUM": "picked_qty", "as": "picked_qty"}],
|
||||
group_by="material_request_item",
|
||||
):
|
||||
data = {d.material_request_item: {"picked_qty": d.picked_qty} for d in data}
|
||||
frappe.db.bulk_update("Material Request Item", data)
|
||||
@@ -139,11 +139,13 @@ frappe.ui.form.on("Material Request", {
|
||||
|
||||
if (flt(frm.doc.per_ordered, precision) < 100) {
|
||||
let add_create_pick_list_button = () => {
|
||||
frm.add_custom_button(
|
||||
__("Pick List"),
|
||||
() => frm.events.create_pick_list(frm),
|
||||
__("Create")
|
||||
);
|
||||
if (frm.doc.items.some((item) => item.stock_qty - item.picked_qty > 0)) {
|
||||
frm.add_custom_button(
|
||||
__("Pick List"),
|
||||
() => frm.events.create_pick_list(frm),
|
||||
__("Create")
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (frm.doc.material_request_type === "Material Transfer") {
|
||||
|
||||
@@ -911,11 +911,7 @@ def raise_work_orders(material_request, company):
|
||||
@frappe.whitelist()
|
||||
def create_pick_list(source_name, target_doc=None):
|
||||
def update_item(obj, target, source_parent):
|
||||
qty = (
|
||||
flt(flt(obj.stock_qty) - flt(obj.ordered_qty)) / target.conversion_factor
|
||||
if flt(obj.stock_qty) > flt(obj.ordered_qty)
|
||||
else 0
|
||||
)
|
||||
qty = flt((obj.stock_qty - obj.picked_qty) / target.conversion_factor, obj.precision("qty"))
|
||||
target.qty = qty
|
||||
target.stock_qty = qty * obj.conversion_factor
|
||||
target.conversion_factor = obj.conversion_factor
|
||||
@@ -931,11 +927,15 @@ def create_pick_list(source_name, target_doc=None):
|
||||
},
|
||||
"Material Request Item": {
|
||||
"doctype": "Pick List Item",
|
||||
"field_map": {"name": "material_request_item", "stock_qty": "stock_qty"},
|
||||
"field_map": {
|
||||
"name": "material_request_item",
|
||||
"stock_qty": "stock_qty",
|
||||
"from_warehouse": "warehouse",
|
||||
},
|
||||
"postprocess": update_item,
|
||||
"condition": lambda doc: (
|
||||
flt(doc.ordered_qty, doc.precision("ordered_qty"))
|
||||
< flt(doc.stock_qty, doc.precision("ordered_qty"))
|
||||
flt(doc.picked_qty, doc.precision("picked_qty"))
|
||||
< flt(doc.stock_qty, doc.precision("stock_qty"))
|
||||
),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
|
||||
import frappe
|
||||
import frappe.model
|
||||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.utils import flt, today
|
||||
|
||||
@@ -1004,6 +1003,25 @@ class TestMaterialRequest(IntegrationTestCase):
|
||||
pl_for_pending = create_pick_list(mr.name)
|
||||
self.assertEqual(pl_for_pending.locations[0].qty, 5)
|
||||
|
||||
def test_mr_pick_list_qty_validation(self):
|
||||
"""Test for checking pick list qty validation from Material Request"""
|
||||
|
||||
mr = make_material_request(material_request_type="Material Transfer")
|
||||
pl = create_pick_list(mr.name)
|
||||
pl.locations[0].qty = 9
|
||||
pl.locations[0].stock_qty = 9
|
||||
pl.submit()
|
||||
|
||||
mr.reload()
|
||||
self.assertEqual(mr.items[0].picked_qty, 9)
|
||||
|
||||
pl = create_pick_list(mr.name)
|
||||
self.assertEqual(pl.locations[0].qty, 1)
|
||||
|
||||
pl.locations[0].qty = 2
|
||||
pl.locations[0].stock_qty = 2
|
||||
self.assertRaises(frappe.ValidationError, pl.submit)
|
||||
|
||||
def test_mr_status_with_partial_and_excess_end_transit(self):
|
||||
material_request = make_material_request(
|
||||
material_request_type="Material Transfer",
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"qty_info_sec_break",
|
||||
"min_order_qty",
|
||||
"projected_qty",
|
||||
"picked_qty",
|
||||
"qty_info_col_break",
|
||||
"actual_qty",
|
||||
"ordered_qty",
|
||||
@@ -518,13 +519,22 @@
|
||||
"fieldtype": "Float",
|
||||
"label": "Projected On Hand",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "picked_qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 1,
|
||||
"label": "Picked Qty",
|
||||
"no_copy": 1,
|
||||
"non_negative": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-12-02 14:14:45.972664",
|
||||
"modified": "2025-12-17 13:47:27.317226",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Material Request Item",
|
||||
|
||||
@@ -41,6 +41,7 @@ class MaterialRequestItem(Document):
|
||||
parent: DF.Data
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
picked_qty: DF.Float
|
||||
price_list_rate: DF.Currency
|
||||
production_plan: DF.Link | None
|
||||
project: DF.Link | None
|
||||
|
||||
@@ -19,7 +19,6 @@ from erpnext.selling.doctype.sales_order.sales_order import (
|
||||
)
|
||||
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
|
||||
get_auto_batch_nos,
|
||||
get_picked_serial_nos,
|
||||
)
|
||||
from erpnext.stock.get_item_details import get_company_total_stock, get_conversion_factor
|
||||
from erpnext.stock.serial_batch_bundle import (
|
||||
@@ -66,6 +65,21 @@ class PickList(TransactionBase):
|
||||
work_order: DF.Link | None
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.status_updater = [
|
||||
{
|
||||
"source_dt": "Pick List Item",
|
||||
"target_dt": "Material Request Item",
|
||||
"target_field": "picked_qty",
|
||||
"target_parent_dt": "Material Request",
|
||||
"target_parent_field": "",
|
||||
"join_field": "material_request_item",
|
||||
"target_ref_field": "stock_qty",
|
||||
"source_field": "stock_qty",
|
||||
}
|
||||
]
|
||||
|
||||
def onload(self) -> None:
|
||||
if frappe.get_cached_value("Stock Settings", None, "enable_stock_reservation"):
|
||||
if self.has_unreserved_stock():
|
||||
@@ -228,6 +242,7 @@ class PickList(TransactionBase):
|
||||
self.update_bundle_picked_qty()
|
||||
self.update_reference_qty()
|
||||
self.update_sales_order_picking_status()
|
||||
self.update_prevdoc_status()
|
||||
|
||||
def validate_expired_batches(self):
|
||||
batches = []
|
||||
@@ -305,6 +320,7 @@ class PickList(TransactionBase):
|
||||
self.update_reference_qty()
|
||||
self.update_sales_order_picking_status()
|
||||
self.delink_serial_and_batch_bundle()
|
||||
self.update_prevdoc_status()
|
||||
|
||||
def delink_serial_and_batch_bundle(self):
|
||||
for row in self.locations:
|
||||
|
||||
@@ -178,7 +178,8 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Material Request",
|
||||
"options": "Material Request",
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "material_request_item",
|
||||
@@ -283,7 +284,7 @@
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-09-23 00:02:57.817040",
|
||||
"modified": "2025-12-18 21:09:12.737036",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Pick List Item",
|
||||
|
||||
Reference in New Issue
Block a user