mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-18 20:49:19 +00:00
feat: Pick list from SO with Product Bundle
This commit is contained in:
@@ -1232,10 +1232,27 @@ def make_inter_company_purchase_order(source_name, target_doc=None):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_pick_list(source_name, target_doc=None):
|
def create_pick_list(source_name, target_doc=None):
|
||||||
def update_item_quantity(source, target, source_parent):
|
from erpnext.stock.doctype.packed_item.packed_item import is_product_bundle
|
||||||
|
|
||||||
|
def update_item_quantity(source, target, source_parent) -> None:
|
||||||
target.qty = flt(source.qty) - flt(source.delivered_qty)
|
target.qty = flt(source.qty) - flt(source.delivered_qty)
|
||||||
target.stock_qty = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.conversion_factor)
|
target.stock_qty = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.conversion_factor)
|
||||||
|
|
||||||
|
def update_packed_item_qty(source, target, source_parent) -> None:
|
||||||
|
qty = flt(source.qty)
|
||||||
|
for item in source_parent.items:
|
||||||
|
if source.parent_detail_docname == item.name:
|
||||||
|
pending_percent = (item.qty - item.delivered_qty) / item.qty
|
||||||
|
target.qty = target.stock_qty = qty * pending_percent
|
||||||
|
return
|
||||||
|
|
||||||
|
def should_pick_order_item(item) -> bool:
|
||||||
|
return (
|
||||||
|
abs(item.delivered_qty) < abs(item.qty)
|
||||||
|
and item.delivered_by_supplier != 1
|
||||||
|
and not is_product_bundle(item.item_code)
|
||||||
|
)
|
||||||
|
|
||||||
doc = get_mapped_doc(
|
doc = get_mapped_doc(
|
||||||
"Sales Order",
|
"Sales Order",
|
||||||
source_name,
|
source_name,
|
||||||
@@ -1245,8 +1262,16 @@ def create_pick_list(source_name, target_doc=None):
|
|||||||
"doctype": "Pick List Item",
|
"doctype": "Pick List Item",
|
||||||
"field_map": {"parent": "sales_order", "name": "sales_order_item"},
|
"field_map": {"parent": "sales_order", "name": "sales_order_item"},
|
||||||
"postprocess": update_item_quantity,
|
"postprocess": update_item_quantity,
|
||||||
"condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty)
|
"condition": should_pick_order_item,
|
||||||
and doc.delivered_by_supplier != 1,
|
},
|
||||||
|
"Packed Item": {
|
||||||
|
"doctype": "Pick List Item",
|
||||||
|
"field_map": {
|
||||||
|
"parent": "sales_order",
|
||||||
|
"name": "sales_order_item",
|
||||||
|
"parent_detail_docname": "product_bundle_item",
|
||||||
|
},
|
||||||
|
"postprocess": update_packed_item_qty,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
target_doc,
|
target_doc,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
"ordered_qty",
|
"ordered_qty",
|
||||||
"column_break_16",
|
"column_break_16",
|
||||||
"incoming_rate",
|
"incoming_rate",
|
||||||
|
"picked_qty",
|
||||||
"page_break",
|
"page_break",
|
||||||
"prevdoc_doctype",
|
"prevdoc_doctype",
|
||||||
"parent_detail_docname"
|
"parent_detail_docname"
|
||||||
@@ -234,13 +235,19 @@
|
|||||||
"label": "Ordered Qty",
|
"label": "Ordered Qty",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "picked_qty",
|
||||||
|
"fieldname": "picked_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Picked Qty"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-03-10 15:42:00.265915",
|
"modified": "2022-04-21 08:05:29.785362",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Packed Item",
|
"name": "Packed Item",
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ def make_packing_list(doc):
|
|||||||
reset = reset_packing_list(doc)
|
reset = reset_packing_list(doc)
|
||||||
|
|
||||||
for item_row in doc.get("items"):
|
for item_row in doc.get("items"):
|
||||||
if frappe.db.exists("Product Bundle", {"new_item_code": item_row.item_code}):
|
if is_product_bundle(item_row.item_code):
|
||||||
for bundle_item in get_product_bundle_items(item_row.item_code):
|
for bundle_item in get_product_bundle_items(item_row.item_code):
|
||||||
pi_row = add_packed_item_row(
|
pi_row = add_packed_item_row(
|
||||||
doc=doc,
|
doc=doc,
|
||||||
@@ -54,6 +54,10 @@ def make_packing_list(doc):
|
|||||||
set_product_bundle_rate_amount(doc, parent_items_price) # set price in bundle item
|
set_product_bundle_rate_amount(doc, parent_items_price) # set price in bundle item
|
||||||
|
|
||||||
|
|
||||||
|
def is_product_bundle(item_code: str) -> bool:
|
||||||
|
return bool(frappe.db.exists("Product Bundle", {"new_item_code": item_code}))
|
||||||
|
|
||||||
|
|
||||||
def get_indexed_packed_items_table(doc):
|
def get_indexed_packed_items_table(doc):
|
||||||
"""
|
"""
|
||||||
Create dict from stale packed items table like:
|
Create dict from stale packed items table like:
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde
|
|||||||
from erpnext.stock.doctype.item.test_item import create_item, make_item
|
from erpnext.stock.doctype.item.test_item import create_item, make_item
|
||||||
from erpnext.stock.doctype.pick_list.pick_list import create_delivery_note
|
from erpnext.stock.doctype.pick_list.pick_list import create_delivery_note
|
||||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||||
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
|
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
|
||||||
EmptyStockReconciliationItemsError,
|
EmptyStockReconciliationItemsError,
|
||||||
)
|
)
|
||||||
@@ -582,16 +583,19 @@ class TestPickList(FrappeTestCase):
|
|||||||
|
|
||||||
def test_picklist_with_bundles(self):
|
def test_picklist_with_bundles(self):
|
||||||
# from test_records.json
|
# from test_records.json
|
||||||
|
warehouse = "_Test Warehouse - _TC"
|
||||||
bundle = "_Test Product Bundle Item"
|
bundle = "_Test Product Bundle Item"
|
||||||
bundle_items = {"_Test Item": 5, "_Test Item Home Desktop 100": 2}
|
bundle_items = {"_Test Item": 5, "_Test Item Home Desktop 100": 2}
|
||||||
|
for item in bundle_items:
|
||||||
|
make_stock_entry(item=item, to_warehouse=warehouse, qty=10, rate=10)
|
||||||
|
|
||||||
so = make_sales_order(item_code=bundle, qty=1)
|
so = make_sales_order(item_code=bundle, qty=3)
|
||||||
|
|
||||||
pl = create_pick_list(so.name)
|
pl = create_pick_list(so.name)
|
||||||
pl.save()
|
pl.save()
|
||||||
self.assertEqual(len(pl.locations), 2)
|
self.assertEqual(len(pl.locations), 2)
|
||||||
for item in pl.locations:
|
for item in pl.locations:
|
||||||
self.assertEqual(item.stock_qty, bundle_items[item.item_code])
|
self.assertEqual(item.stock_qty, bundle_items[item.item_code] * 3)
|
||||||
|
|
||||||
# def test_pick_list_skips_items_in_expired_batch(self):
|
# def test_pick_list_skips_items_in_expired_batch(self):
|
||||||
# pass
|
# pass
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"column_break_15",
|
"column_break_15",
|
||||||
"sales_order",
|
"sales_order",
|
||||||
"sales_order_item",
|
"sales_order_item",
|
||||||
|
"product_bundle_item",
|
||||||
"material_request",
|
"material_request",
|
||||||
"material_request_item"
|
"material_request_item"
|
||||||
],
|
],
|
||||||
@@ -146,6 +147,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "sales_order_item",
|
"fieldname": "sales_order_item",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"hidden": 1,
|
||||||
"label": "Sales Order Item",
|
"label": "Sales Order Item",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@@ -177,11 +179,19 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Item Group",
|
"label": "Item Group",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "product bundle item row's name in sales order. Also indicates that picked item is to be used for a product bundle",
|
||||||
|
"fieldname": "product_bundle_item",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Product Bundle Item",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-09-28 12:02:16.923056",
|
"modified": "2022-04-22 05:27:38.497997",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Pick List Item",
|
"name": "Pick List Item",
|
||||||
@@ -190,5 +200,6 @@
|
|||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user