fix: Pick List Enhancements
diff --git a/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py b/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py
new file mode 100644
index 0000000..4249188
--- /dev/null
+++ b/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py
@@ -0,0 +1,10 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+
+def execute():
+ frappe.db.sql("""UPDATE `tabPick List` set purpose = 'Delivery'
+ WHERE docstatus = 1 and purpose = 'Delivery against Sales Order' """)
\ No newline at end of file
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index e7cbf40..6ccbd51 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -1043,7 +1043,7 @@
},
}, target_doc)
- doc.purpose = 'Delivery against Sales Order'
+ doc.purpose = 'Delivery'
doc.set_item_locations()
diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js
index 2789711..f4fd0a2 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.js
+++ b/erpnext/stock/doctype/pick_list/pick_list.js
@@ -38,11 +38,11 @@
};
});
},
- get_item_locations: (frm) => {
- if (!frm.doc.locations || !frm.doc.locations.length) {
- frappe.msgprint(__('First add items in the Item Locations table'));
+ get_item_locations: (frm, save=false) => {
+ if (!(frm.doc.locations && frm.doc.locations.length)) {
+ frappe.msgprint(__('Add items in the Item Locations table'));
} else {
- frm.call('set_item_locations');
+ frm.call('set_item_locations', {save: save});
}
},
refresh: (frm) => {
@@ -52,8 +52,13 @@
'pick_list_name': frm.doc.name,
'purpose': frm.doc.purpose
}).then(target_document_exists => {
+ frm.set_df_property("locations", "allow_on_submit", target_document_exists ? 0 : 1);
+
if (target_document_exists) return;
- if (frm.doc.purpose === 'Delivery against Sales Order') {
+
+ frm.add_custom_button(__('Update Current Stock'), () => frm.trigger('update_pick_list_stock'));
+
+ if (frm.doc.purpose === 'Delivery') {
frm.add_custom_button(__('Delivery Note'), () => frm.trigger('create_delivery_note'), __('Create'));
} else {
frm.add_custom_button(__('Stock Entry'), () => frm.trigger('create_stock_entry'), __('Create'));
@@ -101,22 +106,34 @@
frm.trigger('add_get_items_button');
},
create_delivery_note: (frm) => {
- frappe.model.open_mapped_doc({
- method: 'erpnext.stock.doctype.pick_list.pick_list.create_delivery_note',
- frm: frm
- });
+ if (!(frm.doc.locations && frm.doc.locations.length)) {
+ frappe.msgprint(__('Add items in the Item Locations table'));
+ } else {
+ frappe.model.open_mapped_doc({
+ method: 'erpnext.stock.doctype.pick_list.pick_list.create_delivery_note',
+ frm: frm
+ });
+ }
+
},
create_stock_entry: (frm) => {
- frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.create_stock_entry', {
- 'pick_list': frm.doc,
- }).then(stock_entry => {
- frappe.model.sync(stock_entry);
- frappe.set_route("Form", 'Stock Entry', stock_entry.name);
- });
+ if (!(frm.doc.locations && frm.doc.locations.length)) {
+ frappe.msgprint(__('Add items in the Item Locations table'));
+ } else {
+ frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.create_stock_entry', {
+ 'pick_list': frm.doc,
+ }).then(stock_entry => {
+ frappe.model.sync(stock_entry);
+ frappe.set_route("Form", 'Stock Entry', stock_entry.name);
+ });
+ }
+ },
+ update_pick_list_stock: (frm) => {
+ frm.events.get_item_locations(frm, true);
},
add_get_items_button: (frm) => {
let purpose = frm.doc.purpose;
- if (purpose != 'Delivery against Sales Order' || frm.doc.docstatus !== 0) return;
+ if (purpose != 'Delivery' || frm.doc.docstatus !== 0) return;
let get_query_filters = {
docstatus: 1,
per_delivered: ['<', 100],
diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json
index 8d5ef3d..c01388d 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.json
+++ b/erpnext/stock/doctype/pick_list/pick_list.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "naming_series:",
"creation": "2019-07-11 16:03:13.681045",
"doctype": "DocType",
@@ -44,7 +45,7 @@
"options": "Warehouse"
},
{
- "depends_on": "eval:doc.purpose==='Delivery against Sales Order'",
+ "depends_on": "eval:doc.purpose==='Delivery'",
"fieldname": "customer",
"fieldtype": "Link",
"in_list_view": 1,
@@ -59,6 +60,7 @@
"options": "Work Order"
},
{
+ "allow_on_submit": 1,
"fieldname": "locations",
"fieldtype": "Table",
"label": "Item Locations",
@@ -86,7 +88,7 @@
"fieldname": "purpose",
"fieldtype": "Select",
"label": "Purpose",
- "options": "Material Transfer for Manufacture\nMaterial Transfer\nDelivery against Sales Order"
+ "options": "Material Transfer for Manufacture\nMaterial Transfer\nDelivery"
},
{
"depends_on": "eval:['Material Transfer', 'Material Issue'].includes(doc.purpose)",
@@ -111,7 +113,8 @@
}
],
"is_submittable": 1,
- "modified": "2019-08-29 21:10:11.572387",
+ "links": [],
+ "modified": "2020-03-17 11:38:41.932875",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List",
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index c4d8c41..d7afaf3 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -29,7 +29,7 @@
frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity')
.format(frappe.bold(item.item_code), frappe.bold(item.idx)))
- def set_item_locations(self):
+ def set_item_locations(self, save=False):
items = self.aggregate_item_qty()
self.item_location_map = frappe._dict()
@@ -43,7 +43,7 @@
item_code = item_doc.item_code
self.item_location_map.setdefault(item_code,
- get_available_item_locations(item_code, from_warehouses, self.item_count_map.get(item_code)))
+ get_available_item_locations(item_code, from_warehouses, self.item_count_map.get(item_code), self.company))
locations = get_items_with_location_and_quantity(item_doc, self.item_location_map)
@@ -59,12 +59,17 @@
location.update(row)
self.append('locations', location)
+ if save:
+ self.save()
+
def aggregate_item_qty(self):
locations = self.get('locations')
self.item_count_map = {}
# aggregate qty for same item
item_map = OrderedDict()
for item in locations:
+ if not item.item_code:
+ frappe.throw("Row #{0}: Item Code is Mandatory".format(item.idx))
item_code = item.item_code
reference = item.sales_order_item or item.material_request_item
key = (item_code, item.uom, reference)
@@ -130,14 +135,14 @@
item_location_map[item_doc.item_code] = available_locations
return locations
-def get_available_item_locations(item_code, from_warehouses, required_qty):
+def get_available_item_locations(item_code, from_warehouses, required_qty, company):
locations = []
if frappe.get_cached_value('Item', item_code, 'has_serial_no'):
- locations = get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty)
+ locations = get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty, company)
elif frappe.get_cached_value('Item', item_code, 'has_batch_no'):
- locations = get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty)
+ locations = get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty, company)
else:
- locations = get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty)
+ locations = get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty, company)
total_qty_available = sum(location.get('qty') for location in locations)
@@ -150,9 +155,10 @@
return locations
-def get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty):
+def get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty, company):
filters = frappe._dict({
'item_code': item_code,
+ 'company': company,
'warehouse': ['!=', '']
})
@@ -180,7 +186,7 @@
return locations
-def get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty):
+def get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty, company):
warehouse_condition = 'and warehouse in %(warehouses)s' if from_warehouses else ''
batch_locations = frappe.db.sql("""
SELECT
@@ -192,6 +198,7 @@
WHERE
sle.batch_no = batch.name
and sle.`item_code`=%(item_code)s
+ and sle.`company` = %(company)s
and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s
{warehouse_condition}
GROUP BY
@@ -202,16 +209,20 @@
ORDER BY IFNULL(batch.`expiry_date`, '2200-01-01'), batch.`creation`
""".format(warehouse_condition=warehouse_condition), { #nosec
'item_code': item_code,
+ 'company': company,
'today': today(),
'warehouses': from_warehouses
}, as_dict=1)
return batch_locations
-def get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty):
+def get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty, company):
# gets all items available in different warehouses
+ warehouses = [x.get('name') for x in frappe.get_list("Warehouse", {'company': company}, "name")]
+
filters = frappe._dict({
'item_code': item_code,
+ 'warehouse': ['in', warehouses],
'actual_qty': ['>', 0]
})
@@ -230,7 +241,7 @@
@frappe.whitelist()
def create_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.locations]
+ sales_orders = [d.sales_order for d in pick_list.locations if d.sales_order]
sales_orders = set(sales_orders)
delivery_note = None
@@ -238,6 +249,10 @@
delivery_note = create_delivery_note_from_sales_order(sales_order,
delivery_note, skip_item_mapping=True)
+ # map rows without sales orders as well
+ if not delivery_note:
+ delivery_note = frappe.new_doc("Delivery Note")
+
item_table_mapper = {
'doctype': 'Delivery Note Item',
'field_map': {
@@ -248,9 +263,25 @@
'condition': lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
}
+ item_table_mapper_without_so = {
+ 'doctype': 'Delivery Note Item',
+ 'field_map': {
+ 'rate': 'rate',
+ 'name': 'name',
+ 'parent': '',
+ }
+ }
+
for location in pick_list.locations:
- sales_order_item = frappe.get_cached_doc('Sales Order Item', location.sales_order_item)
- dn_item = map_child_doc(sales_order_item, delivery_note, item_table_mapper)
+ if location.sales_order_item:
+ sales_order_item = frappe.get_cached_doc('Sales Order Item', {'name':location.sales_order_item})
+ else:
+ sales_order_item = None
+
+ source_doc, table_mapper = [sales_order_item, item_table_mapper] if sales_order_item \
+ else [location, item_table_mapper_without_so]
+
+ dn_item = map_child_doc(source_doc, delivery_note, table_mapper)
if dn_item:
dn_item.warehouse = location.warehouse
@@ -258,7 +289,7 @@
dn_item.batch_no = location.batch_no
dn_item.serial_no = location.serial_no
- update_delivery_note_item(sales_order_item, dn_item, delivery_note)
+ update_delivery_note_item(source_doc, dn_item, delivery_note)
set_delivery_note_missing_values(delivery_note)
@@ -318,7 +349,7 @@
@frappe.whitelist()
def target_document_exists(pick_list_name, purpose):
- if purpose == 'Delivery against Sales Order':
+ if purpose == 'Delivery':
return frappe.db.exists('Delivery Note', {
'pick_list': pick_list_name
})
diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item.json
index c7a35df..71fbf9a 100644
--- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json
+++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2019-07-11 16:01:22.832885",
"doctype": "DocType",
"editable_grid": 1,
@@ -8,6 +9,7 @@
"item_name",
"column_break_2",
"description",
+ "item_group",
"section_break_5",
"warehouse",
"quantity_section",
@@ -120,7 +122,8 @@
"fieldtype": "Link",
"in_list_view": 1,
"label": "Item",
- "options": "Item"
+ "options": "Item",
+ "reqd": 1
},
{
"fieldname": "quantity_section",
@@ -166,10 +169,18 @@
"fieldtype": "Data",
"label": "Material Request Item",
"read_only": 1
+ },
+ {
+ "fetch_from": "item_code.item_group",
+ "fieldname": "item_group",
+ "fieldtype": "Data",
+ "label": "Item Group",
+ "read_only": 1
}
],
"istable": 1,
- "modified": "2019-08-29 21:28:39.539007",
+ "links": [],
+ "modified": "2020-03-13 19:08:21.995986",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List Item",