Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support product bundles in picklist #30762

Merged
merged 19 commits into from
Apr 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion erpnext/selling/doctype/sales_order/sales_order.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
}
}

this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
if (flt(doc.per_picked, 6) < 100 && flt(doc.per_delivered, 6) < 100) {
this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
}

const order_is_a_sale = ["Sales", "Shopping Cart"].indexOf(doc.order_type) !== -1;
const order_is_maintenance = ["Maintenance"].indexOf(doc.order_type) !== -1;
Expand Down
3 changes: 2 additions & 1 deletion erpnext/selling/doctype/sales_order/sales_order.json
Original file line number Diff line number Diff line change
Expand Up @@ -1520,14 +1520,15 @@
"fieldname": "per_picked",
"fieldtype": "Percent",
"label": "% Picked",
"no_copy": 1,
"read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2022-03-15 21:38:31.437586",
"modified": "2022-04-21 08:16:48.316074",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",
Expand Down
50 changes: 45 additions & 5 deletions erpnext/selling/doctype/sales_order/sales_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,16 @@ def update_delivery_status(self):
if tot_qty != 0:
self.db_set("per_delivered", flt(delivered_qty / tot_qty) * 100, update_modified=False)

def update_picking_status(self):
total_picked_qty = 0.0
total_qty = 0.0
for so_item in self.items:
total_picked_qty += flt(so_item.picked_qty)
total_qty += flt(so_item.stock_qty)
per_picked = total_picked_qty / total_qty * 100

self.db_set("per_picked", flt(per_picked), update_modified=False)

def set_indicator(self):
"""Set indicator for portal"""
if self.per_billed < 100 and self.per_delivered < 100:
Expand Down Expand Up @@ -1232,9 +1242,30 @@ def make_inter_company_purchase_order(source_name, target_doc=None):

@frappe.whitelist()
def create_pick_list(source_name, target_doc=None):
def update_item_quantity(source, target, source_parent):
target.qty = flt(source.qty) - flt(source.delivered_qty)
target.stock_qty = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.conversion_factor)
from erpnext.stock.doctype.packed_item.packed_item import is_product_bundle

def update_item_quantity(source, target, source_parent) -> None:
picked_qty = flt(source.picked_qty) / (flt(source.conversion_factor) or 1)
qty_to_be_picked = flt(source.qty) - max(picked_qty, flt(source.delivered_qty))

target.qty = qty_to_be_picked
target.stock_qty = qty_to_be_picked * 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:
picked_qty = flt(item.picked_qty) / (flt(item.conversion_factor) or 1)
pending_percent = (item.qty - max(picked_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(
"Sales Order",
Expand All @@ -1245,8 +1276,17 @@ def update_item_quantity(source, target, source_parent):
"doctype": "Pick List Item",
"field_map": {"parent": "sales_order", "name": "sales_order_item"},
"postprocess": update_item_quantity,
"condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty)
and doc.delivered_by_supplier != 1,
"condition": should_pick_order_item,
},
"Packed Item": {
"doctype": "Pick List Item",
"field_map": {
"parent": "sales_order",
"name": "sales_order_item",
"parent_detail_docname": "product_bundle_item",
},
"field_no_map": ["picked_qty"],
"postprocess": update_packed_item_qty,
},
},
target_doc,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -803,13 +803,15 @@
{
"fieldname": "picked_qty",
"fieldtype": "Float",
"label": "Picked Qty"
"label": "Picked Qty (in Stock UOM)",
"no_copy": 1,
"read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2022-03-15 20:17:33.984799",
"modified": "2022-04-27 03:15:34.366563",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Item",
Expand Down
10 changes: 9 additions & 1 deletion erpnext/stock/doctype/packed_item/packed_item.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"ordered_qty",
"column_break_16",
"incoming_rate",
"picked_qty",
"page_break",
"prevdoc_doctype",
"parent_detail_docname"
Expand Down Expand Up @@ -234,13 +235,20 @@
"label": "Ordered Qty",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "picked_qty",
"fieldtype": "Float",
"label": "Picked Qty",
"no_copy": 1,
"read_only": 1
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2022-03-10 15:42:00.265915",
"modified": "2022-04-27 05:23:08.683245",
"modified_by": "Administrator",
"module": "Stock",
"name": "Packed Item",
Expand Down
6 changes: 5 additions & 1 deletion erpnext/stock/doctype/packed_item/packed_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def make_packing_list(doc):
reset = reset_packing_list(doc)

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):
pi_row = add_packed_item_row(
doc=doc,
Expand All @@ -54,6 +54,10 @@ def make_packing_list(doc):
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):
"""
Create dict from stale packed items table like:
Expand Down
52 changes: 34 additions & 18 deletions erpnext/stock/doctype/packed_item/test_packed_item.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,58 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt

from typing import List, Optional, Tuple

import frappe
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_to_date, nowdate

from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry


def create_product_bundle(
quantities: Optional[List[int]] = None, warehouse: Optional[str] = None
) -> Tuple[str, List[str]]:
"""Get a new product_bundle for use in tests.

Create 10x required stock if warehouse is specified.
"""
if not quantities:
quantities = [2, 2]

bundle = make_item(properties={"is_stock_item": 0}).name

bundle_doc = frappe.get_doc({"doctype": "Product Bundle", "new_item_code": bundle})

components = []
for qty in quantities:
compoenent = make_item().name
components.append(compoenent)
bundle_doc.append("items", {"item_code": compoenent, "qty": qty})
if warehouse:
make_stock_entry(item=compoenent, to_warehouse=warehouse, qty=10 * qty, rate=100)

bundle_doc.insert()

return bundle, components


class TestPackedItem(FrappeTestCase):
"Test impact on Packed Items table in various scenarios."

@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
cls.warehouse = "_Test Warehouse - _TC"
cls.bundle = "_Test Product Bundle X"
cls.bundle_items = ["_Test Bundle Item 1", "_Test Bundle Item 2"]

cls.bundle2 = "_Test Product Bundle Y"
cls.bundle2_items = ["_Test Bundle Item 3", "_Test Bundle Item 4"]

make_item(cls.bundle, {"is_stock_item": 0})
make_item(cls.bundle2, {"is_stock_item": 0})
for item in cls.bundle_items + cls.bundle2_items:
make_item(item, {"is_stock_item": 1})

make_item("_Test Normal Stock Item", {"is_stock_item": 1})

make_product_bundle(cls.bundle, cls.bundle_items, qty=2)
make_product_bundle(cls.bundle2, cls.bundle2_items, qty=2)
cls.bundle, cls.bundle_items = create_product_bundle(warehouse=cls.warehouse)
cls.bundle2, cls.bundle2_items = create_product_bundle(warehouse=cls.warehouse)

for item in cls.bundle_items + cls.bundle2_items:
make_stock_entry(item_code=item, to_warehouse=cls.warehouse, qty=100, rate=100)
cls.normal_item = make_item().name

def test_adding_bundle_item(self):
"Test impact on packed items if bundle item row is added."
Expand All @@ -58,7 +74,7 @@ def test_updating_bundle_item(self):
self.assertEqual(so.packed_items[1].qty, 4)

# change item code to non bundle item
so.items[0].item_code = "_Test Normal Stock Item"
so.items[0].item_code = self.normal_item
so.save()

self.assertEqual(len(so.packed_items), 0)
Expand Down
4 changes: 3 additions & 1 deletion erpnext/stock/doctype/pick_list/pick_list.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
"set_only_once": 1
},
{
"collapsible": 1,
"fieldname": "print_settings_section",
"fieldtype": "Section Break",
"label": "Print Settings"
Expand All @@ -129,7 +130,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2021-10-05 15:08:40.369957",
"modified": "2022-04-21 07:56:40.646473",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List",
Expand Down Expand Up @@ -199,5 +200,6 @@
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
Loading