fix: Warehouse selection login

- Rename item_loactions -> locations, reference_items -> items
- Add customer and work_order field
- fix "Get Items" button UX
- fix get_pending_work_orders query

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
This commit is contained in:
Suraj Shetty 2019-08-21 09:03:26 +05:30
parent dd63b656c7
commit d79a16c9f3
5 changed files with 126 additions and 82 deletions

View File

@ -710,7 +710,7 @@ def get_work_order_operation_data(work_order, operation, workstation):
return d
@frappe.whitelist()
def make_pick_list(source_name, target_doc=None):
def create_pick_list(source_name, target_doc=None):
def update_item_quantity(source, target, source_parent):
qty = source.required_qty - source.transferred_qty
target.qty = qty

View File

@ -11,17 +11,33 @@ frappe.ui.form.on('Pick List', {
}
};
});
frm.set_query('work_order', () => {
return {
query: 'erpnext.stock.doctype.pick_list.pick_list.get_pending_work_orders',
filters: {
'company': frm.doc.company
}
};
});
},
refresh: (frm) => {
frm.add_custom_button(__('Delivery Note'), () => frm.trigger('make_delivery_note'), __('Create'));
frm.trigger('add_get_items_button');
if (frm.doc.reference_items && frm.doc.reference_items.length) {
if (frm.doc.items && (frm.doc.items.length > 1 || frm.doc.items[0].item_code)) {
frm.add_custom_button(__('Get Item Locations'), () => {
frm.call('set_item_locations');
});
}).addClass('btn-primary');
}
frm.trigger('add_get_items_button');
frm.add_custom_button(__('Delivery Note'), () => frm.trigger('make_delivery_note'), __('Create'));
},
work_order: (frm) => {
frm.clear_table('items');
erpnext.utils.map_current_doc({
method: 'erpnext.manufacturing.doctype.work_order.work_order.create_pick_list',
target: frm,
source_name: frm.doc.work_order
});
},
items_based_on: (frm) => {
frm.trigger('add_get_items_button');
@ -33,40 +49,29 @@ frappe.ui.form.on('Pick List', {
});
},
add_get_items_button(frm) {
frm.remove_custom_button(__("Get items"));
let source_doctype = frm.doc.items_based_on;
let date_field = 'transaction_date';
let get_query_method = null;
if (source_doctype != 'Sales Order') return;
let get_query_filters = {
docstatus: 1,
per_delivered: ['<', 100],
status: ['!=', '']
status: ['!=', ''],
customer: frm.doc.customer
};
let method = 'erpnext.selling.doctype.sales_order.sales_order.make_pick_list';
if (source_doctype === 'Work Order') {
method = 'erpnext.manufacturing.doctype.work_order.work_order.make_pick_list';
date_field = 'planned_start_date';
get_query_method = 'erpnext.stock.doctype.pick_list.pick_list.get_pending_work_orders';
get_query_filters = null;
}
let get_query = () => {
return {
'query': get_query_method,
'filters': get_query_filters
};
};
frm.add_custom_button(__("Get items"), () => {
frm.get_items_btn = frm.add_custom_button(__('Get Items'), () => {
if (!frm.doc.customer) {
frappe.msgprint(__('Please select Customer first'));
return;
}
erpnext.utils.map_current_doc({
method: method,
source_doctype: source_doctype,
method: 'erpnext.selling.doctype.sales_order.sales_order.make_pick_list',
source_doctype: 'Sales Order',
target: frm,
setters: {
company: frm.doc.company,
customer: frm.doc.customer
},
date_field: date_field,
get_query: get_query
date_field: 'transaction_date',
get_query_filters: get_query_filters
});
});
}

View File

@ -5,21 +5,25 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"company",
"column_break_4",
"items_based_on",
"customer",
"work_order",
"column_break_4",
"parent_warehouse",
"company",
"section_break_4",
"reference_items",
"items",
"section_break_6",
"item_locations"
"locations"
],
"fields": [
{
"fieldname": "company",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Company",
"options": "Company"
"options": "Company",
"reqd": 1
},
{
"fieldname": "column_break_4",
@ -41,26 +45,45 @@
"options": "Warehouse"
},
{
"fieldname": "item_locations",
"default": "Work Order",
"fieldname": "items_based_on",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Items Based On",
"options": "Sales Order\nWork Order",
"reqd": 1
},
{
"depends_on": "eval:doc.items_based_on===\"Sales Order\"",
"fieldname": "customer",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Customer",
"options": "Customer",
"reqd": 1
},
{
"depends_on": "eval:doc.items_based_on===\"Work Order\"",
"fieldname": "work_order",
"fieldtype": "Link",
"label": "Work Order",
"options": "Work Order"
},
{
"fieldname": "items",
"fieldtype": "Table",
"label": "Items To Be Picked",
"options": "Pick List Reference Item",
"reqd": 1
},
{
"fieldname": "locations",
"fieldtype": "Table",
"label": "Item Locations",
"options": "Pick List Item"
},
{
"fieldname": "reference_items",
"fieldtype": "Table",
"label": "Items To Be Picked",
"options": "Pick List Reference Item"
},
{
"default": "Sales Order",
"fieldname": "items_based_on",
"fieldtype": "Select",
"label": "Items Based On",
"options": "Sales Order\nWork Order"
}
],
"modified": "2019-08-19 12:31:54.023456",
"modified": "2019-08-20 16:57:11.006221",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List",

View File

@ -14,25 +14,29 @@ from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note a
class PickList(Document):
def set_item_locations(self):
reference_items = self.reference_items
items = self.items
self.item_location_map = frappe._dict()
from_warehouses = None
if self.parent_warehouse:
from_warehouses = frappe.db.get_descendants('Warehouse', self.parent_warehouse)
# Reset
self.delete_key('item_locations')
for item_doc in reference_items:
if frappe.get_cached_value('Item', item_doc.item_code, 'has_serial_no'):
item_locations = get_item_locations_based_on_serial_nos(item_doc)
elif frappe.get_cached_value('Item', item_doc.item_code, 'has_batch_no'):
item_locations = get_item_locations_based_on_batch_nos(item_doc)
self.delete_key('locations')
for item_doc in items:
item_code = item_doc.item_code
if frappe.get_cached_value('Item', item_code, 'has_serial_no'):
locations = get_item_locations_based_on_serial_nos(item_doc)
elif frappe.get_cached_value('Item', item_code, 'has_batch_no'):
locations = get_item_locations_based_on_batch_nos(item_doc)
else:
item_locations = get_items_with_warehouse_and_quantity(item_doc, from_warehouses)
if item_code not in self.item_location_map:
self.item_location_map[item_code] = get_available_items(item_code, from_warehouses)
locations = get_items_with_warehouse_and_quantity(item_doc, from_warehouses, self.item_location_map)
for row in item_locations:
for row in locations:
row.update({
'item_code': item_doc.item_code,
'item_code': item_code,
'sales_order': item_doc.sales_order,
'sales_order_item': item_doc.sales_order_item,
'uom': item_doc.uom,
@ -41,14 +45,15 @@ class PickList(Document):
'stock_qty': row.get("qty", 0) * item_doc.conversion_factor,
'picked_qty': row.get("qty", 0) * item_doc.conversion_factor
})
self.append('item_locations', row)
self.append('locations', row)
def get_items_with_warehouse_and_quantity(item_doc, from_warehouses):
item_locations = []
item_location_map = get_available_items(item_doc.item_code, from_warehouses)
def get_items_with_warehouse_and_quantity(item_doc, from_warehouses, item_location_map):
available_locations = item_location_map.get(item_doc.item_code)
locations = []
remaining_stock_qty = item_doc.stock_qty
while remaining_stock_qty > 0 and item_location_map:
item_location = item_location_map.pop(0)
while remaining_stock_qty > 0 and available_locations:
item_location = available_locations.pop(0)
stock_qty = remaining_stock_qty if item_location.qty >= remaining_stock_qty else item_location.qty
qty = stock_qty / (item_doc.conversion_factor or 1)
@ -57,16 +62,25 @@ def get_items_with_warehouse_and_quantity(item_doc, from_warehouses):
qty = floor(qty)
stock_qty = qty * item_doc.conversion_factor
item_locations.append({
locations.append({
'qty': qty,
'warehouse': item_location.warehouse
})
remaining_stock_qty -= stock_qty
qty_diff = item_location.qty - stock_qty
# if extra quantity is available push current warehouse to available locations
if qty_diff:
item_location.qty = qty_diff
available_locations = [item_location] + available_locations
if remaining_stock_qty:
frappe.msgprint('{0} {1} of {2} is not available.'
.format(remaining_stock_qty / item_doc.conversion_factor, item_doc.uom, item_doc.item_code))
return item_locations
# update available locations for the item
item_location_map[item_doc.item_code] = available_locations
return locations
def get_available_items(item_code, from_warehouses):
# gets all items available in different warehouses
@ -110,15 +124,15 @@ def get_item_locations_based_on_serial_nos(item_doc):
for serial_no, warehouse in serial_nos:
warehouse_serial_nos_map.setdefault(warehouse, []).append(serial_no)
item_locations = []
locations = []
for warehouse, serial_nos in iteritems(warehouse_serial_nos_map):
item_locations.append({
locations.append({
'qty': len(serial_nos),
'warehouse': warehouse,
'serial_no': '\n'.join(serial_nos)
})
return item_locations
return locations
def get_item_locations_based_on_batch_nos(item_doc):
batch_qty = frappe.db.sql("""
@ -143,7 +157,7 @@ def get_item_locations_based_on_batch_nos(item_doc):
'today': today()
}, as_dict=1)
item_locations = []
locations = []
required_qty = item_doc.qty
for d in batch_qty:
if d.qty > required_qty:
@ -151,7 +165,7 @@ def get_item_locations_based_on_batch_nos(item_doc):
else:
required_qty -= d.qty
item_locations.append(d)
locations.append(d)
if required_qty <= 0:
break
@ -159,12 +173,12 @@ def get_item_locations_based_on_batch_nos(item_doc):
if required_qty:
frappe.msgprint('No batches found for {} qty of {}.'.format(required_qty, item_doc.item_code))
return item_locations
return locations
@frappe.whitelist()
def make_delivery_note(source_name, target_doc=None):
pick_list = frappe.get_doc('Pick List', source_name)
sales_orders = [d.sales_order for d in pick_list.item_locations]
sales_orders = [d.sales_order for d in pick_list.locations]
sales_orders = set(sales_orders)
delivery_note = None
@ -172,7 +186,7 @@ def make_delivery_note(source_name, target_doc=None):
delivery_note = make_delivery_note_from_sales_order(sales_order,
delivery_note, skip_item_mapping=True)
for location in pick_list.item_locations:
for location in pick_list.locations:
sales_order_item = frappe.get_cached_doc('Sales Order Item', location.sales_order_item)
item_table_mapper = {
"doctype": "Delivery Note Item",
@ -232,10 +246,11 @@ def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filte
FROM
`tabWork Order`
WHERE
`qty` > `produced_qty`
AND `status` not in ('Completed', 'Stopped')
AND name like %(txt)s
AND docstatus = 1
`status` not in ('Completed', 'Stopped')
AND `qty` > `produced_qty`
AND `docstatus` = 1
AND `company` = %(company)s
AND `name` like %(txt)s
ORDER BY
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), name
LIMIT
@ -245,4 +260,5 @@ def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filte
'_txt': txt.replace('%', ''),
'start': start,
'page_length': frappe.utils.cint(page_length),
'company': filters.get('company')
}, as_dict=as_dict)

View File

@ -14,7 +14,7 @@ class TestPickList(unittest.TestCase):
pick_list = frappe.get_doc({
'doctype': 'Pick List',
'company': '_Test Company',
'reference_items': [{
'items': [{
'item': '_Test Item Home Desktop 100',
'reference_doctype': 'Sales Order',
'qty': 5,
@ -32,7 +32,7 @@ class TestPickList(unittest.TestCase):
pick_list = frappe.get_doc({
'doctype': 'Pick List',
'company': '_Test Company',
'reference_items': [{
'items': [{
'item': '_Test Item Warehouse Group Wise Reorder',
'reference_doctype': 'Sales Order',
'qty': 1000,
@ -68,7 +68,7 @@ class TestPickList(unittest.TestCase):
pick_list = frappe.get_doc({
'doctype': 'Pick List',
'company': '_Test Company',
'reference_items': [{
'items': [{
'item': '_Test Serialized Item',
'reference_doctype': 'Sales Order',
'qty': 1000,