Merge branch 'develop' of github.com:frappe/erpnext into feature-pick-list
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 39dda92..89739cc 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -102,6 +102,7 @@
 erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
 	onload: function(doc, dt, dn) {
 		this._super();
+		this.frm.add_custom_button(__('Pick Ticket'), () => this.make_pick_ticket(), __('Create'));
 	},
 
 	refresh: function(doc, dt, dn) {
@@ -233,6 +234,13 @@
 		this.order_type(doc);
 	},
 
+	make_pick_ticket() {
+		frappe.model.open_mapped_doc({
+			method: "erpnext.selling.doctype.sales_order.sales_order.make_pick_ticket",
+			frm: this.frm
+		})
+	},
+
 	make_work_order() {
 		var me = this;
 		this.frm.call({
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 09dc9a9..97a9739 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -996,3 +996,24 @@
 def make_inter_company_purchase_order(source_name, target_doc=None):
 	from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
 	return make_inter_company_transaction("Sales Order", source_name, target_doc)
+
+@frappe.whitelist()
+def make_pick_ticket(source_name, target_doc=None):
+	doc = get_mapped_doc("Sales Order", source_name, {
+		"Sales Order": {
+			"doctype": "Pick Ticket",
+			"validation": {
+				"docstatus": ["=", 1]
+			}
+		},
+		"Sales Order Item": {
+			"doctype": "Pick Ticket Reference Item",
+			"field_map": {
+				"item_code": "item",
+				"parenttype": "reference_doctype",
+				"parent": "reference_name"
+			},
+		},
+	}, target_doc)
+
+	return doc
diff --git a/erpnext/stock/doctype/pick_ticket/__init__.py b/erpnext/stock/doctype/pick_ticket/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/doctype/pick_ticket/__init__.py
diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.js b/erpnext/stock/doctype/pick_ticket/pick_ticket.js
new file mode 100644
index 0000000..a2d6cd7
--- /dev/null
+++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.js
@@ -0,0 +1,29 @@
+// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Pick Ticket', {
+	refresh: (frm) => {
+		this.frm.add_custom_button(__('Sales Order'), function() {
+			erpnext.utils.map_current_doc({
+				method: "erpnext.selling.doctype.sales_order.sales_order.make_pick_ticket",
+				source_doctype: "Sales Order",
+				target: frm,
+				setters: {
+					company: frm.doc.company || undefined,
+				},
+				get_query_filters: {
+					docstatus: 1,
+				}
+			});
+		}, __("Get items from"));
+
+		frm.add_custom_button(__('Get Item Locations'), () => {
+			frm.trigger('set_item_locations');
+		});
+	},
+
+	set_item_locations: (frm) => {
+		frm.call('set_item_locations')
+	}
+
+});
diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.json b/erpnext/stock/doctype/pick_ticket/pick_ticket.json
new file mode 100644
index 0000000..9351925
--- /dev/null
+++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.json
@@ -0,0 +1,80 @@
+{
+ "autoname": "PICK.####",
+ "creation": "2019-07-11 16:03:13.681045",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "column_break_4",
+  "group_warehouse",
+  "section_break_4",
+  "reference_document_items",
+  "section_break_6",
+  "items"
+ ],
+ "fields": [
+  {
+   "fieldname": "items",
+   "fieldtype": "Table",
+   "label": "Items Locations",
+   "options": "Pick Ticket Item",
+   "read_only": 1
+  },
+  {
+   "description": "Items under this warehouse will be suggested",
+   "fieldname": "group_warehouse",
+   "fieldtype": "Link",
+   "label": "Group Warehouse",
+   "options": "Warehouse"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_6",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break",
+   "label": "Reference Items"
+  },
+  {
+   "fieldname": "reference_document_items",
+   "fieldtype": "Table",
+   "label": "Reference Document Items",
+   "options": "Pick Ticket Reference Item"
+  }
+ ],
+ "modified": "2019-07-26 12:06:08.941760",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Pick Ticket",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.py b/erpnext/stock/doctype/pick_ticket/pick_ticket.py
new file mode 100644
index 0000000..18382e6
--- /dev/null
+++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class PickTicket(Document):
+	def set_item_locations(self):
+		reference_items = self.reference_document_items
+		self.delete_key('items')
+		for item in reference_items:
+			data = get_items_with_warehouse_and_quantity(item)
+
+			for item_info in data:
+				self.append('items', item_info)
+
+		for item in self.get('items'):
+			if frappe.get_cached_value('Item', item.item, 'has_serial_no'):
+				set_serial_nos(item)
+			elif frappe.get_cached_value('Item', item.item, 'has_batch_no'):
+				set_batch_no(item, self)
+
+def get_available_items(item):
+	# gets all items available in different warehouses
+	# FIFO
+	available_items = frappe.get_all('Bin', filters={
+		'item_code': item,
+		'actual_qty': ['>', 0]
+	}, fields=['warehouse', 'actual_qty as qty'], order_by='creation')
+
+	return available_items
+
+def get_items_with_warehouse_and_quantity(item_doc):
+	items = []
+	item_locations = get_available_items(item_doc.item)
+	remaining_qty = item_doc.qty
+
+
+	while remaining_qty > 0 and item_locations:
+		item_location = item_locations.pop(0)
+		qty = remaining_qty if item_location.qty >= remaining_qty else item_location.qty
+		items.append({
+			'item': item_doc.item,
+			'qty': qty,
+			'warehouse': item_location.warehouse,
+			'reference_doctype': item_doc.reference_doctype,
+			'reference_name': item_doc.reference_name
+		})
+		remaining_qty -= qty
+
+	if remaining_qty:
+		print('---------- {} qty of {} is out of stock. Skipping... -------------'.format(remaining_qty, item_doc.item))
+		return items
+
+	return items
+
+def set_serial_nos(item):
+	serial_nos = frappe.get_all('Serial No', {
+		'item_code': item.item,
+		'warehouse': item.warehouse
+	}, limit=item.qty, order_by='purchase_date')
+	item.set('serial_no', '\n'.join([serial_no.name for serial_no in serial_nos]))
+
+def set_batch_no(item, doc):
+	batches = frappe.get_all('Stock Ledger Entry',
+		fields=['batch_no', 'sum(actual_qty) as qty'],
+		filters={
+			'item_code': item.item,
+			'warehouse': item.warehouse
+		},
+		group_by='warehouse, batch_no, item_code')
+
+	if batches:
+		# TODO: check expiry and split item if batch is more than 1
+		item.batch_no = batches[0].batch_no
\ No newline at end of file
diff --git a/erpnext/stock/doctype/pick_ticket/test_pick_ticket.py b/erpnext/stock/doctype/pick_ticket/test_pick_ticket.py
new file mode 100644
index 0000000..3fee6db
--- /dev/null
+++ b/erpnext/stock/doctype/pick_ticket/test_pick_ticket.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestPickTicket(unittest.TestCase):
+	def test_pick_list_picks_warehouse_for_each_item():
+		pass
+
+	def test_pick_list_skips_out_of_warranty_item():
+		pass
+
+	def test_pick_list_skips_items_in_expired_batch():
+		pass
+
+	def test_pick_list_shows_serial_no_for_serialized_item():
+		pass
+
+	def test_pick_list_for_multiple_reference_doctypes():
+		pass
diff --git a/erpnext/stock/doctype/pick_ticket_item/__init__.py b/erpnext/stock/doctype/pick_ticket_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/doctype/pick_ticket_item/__init__.py
diff --git a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json
new file mode 100644
index 0000000..7095be6
--- /dev/null
+++ b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json
@@ -0,0 +1,142 @@
+{
+ "creation": "2019-07-11 16:01:22.832885",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "item",
+  "item_name",
+  "column_break_2",
+  "description",
+  "has_batch_no",
+  "has_serial_no",
+  "section_break_5",
+  "warehouse",
+  "qty",
+  "picked_qty",
+  "serial_no",
+  "batch_no",
+  "reference_section",
+  "reference_doctype",
+  "reference_name",
+  "reference_document_item"
+ ],
+ "fields": [
+  {
+   "fieldname": "item",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item",
+   "options": "Item"
+  },
+  {
+   "fieldname": "qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Qty",
+   "read_only": 1
+  },
+  {
+   "fieldname": "picked_qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Picked Qty"
+  },
+  {
+   "fieldname": "warehouse",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Warehouse",
+   "options": "Warehouse",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "item.item_name",
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "item.description",
+   "fieldname": "description",
+   "fieldtype": "Text",
+   "label": "Description",
+   "read_only": 1
+  },
+  {
+   "fieldname": "reference_document_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Reference Document Item",
+   "read_only": 1
+  },
+  {
+   "fieldname": "serial_no",
+   "fieldtype": "Small Text",
+   "label": "Serial No",
+   "read_only": 1
+  },
+  {
+   "fieldname": "batch_no",
+   "fieldtype": "Link",
+   "label": "Batch No",
+   "options": "Batch",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "item.has_serial_no",
+   "fieldname": "has_serial_no",
+   "fieldtype": "Check",
+   "label": "Has Serial No",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "item.has_batch_no",
+   "fieldname": "has_batch_no",
+   "fieldtype": "Check",
+   "label": "Has Batch No",
+   "read_only": 1
+  },
+  {
+   "fieldname": "reference_section",
+   "fieldtype": "Section Break",
+   "label": "Reference"
+  },
+  {
+   "fieldname": "reference_doctype",
+   "fieldtype": "Select",
+   "label": "Reference Document Type",
+   "options": "Sales Order\nWork Order",
+   "read_only": 1
+  },
+  {
+   "fieldname": "reference_name",
+   "fieldtype": "Dynamic Link",
+   "label": "Reference Document",
+   "options": "reference_doctype",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_5",
+   "fieldtype": "Section Break"
+  }
+ ],
+ "istable": 1,
+ "modified": "2019-07-26 14:47:33.965373",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Pick Ticket Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.py b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.py
new file mode 100644
index 0000000..a13666a
--- /dev/null
+++ b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class PickTicketItem(Document):
+	pass
diff --git a/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.js b/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.js
new file mode 100644
index 0000000..a3f9096
--- /dev/null
+++ b/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Pick Ticket Reference Item', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.json b/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.json
new file mode 100644
index 0000000..ae7ea35
--- /dev/null
+++ b/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.json
@@ -0,0 +1,48 @@
+{
+ "creation": "2019-07-24 16:11:07.415562",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "item",
+  "qty",
+  "reference_doctype",
+  "reference_name"
+ ],
+ "fields": [
+  {
+   "fieldname": "item",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item",
+   "options": "Item"
+  },
+  {
+   "fieldname": "reference_doctype",
+   "fieldtype": "Link",
+   "label": "Reference Document type",
+   "options": "DocType"
+  },
+  {
+   "fieldname": "reference_name",
+   "fieldtype": "Dynamic Link",
+   "label": "Reference Name",
+   "options": "reference_doctype"
+  },
+  {
+   "fieldname": "qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Qty"
+  }
+ ],
+ "istable": 1,
+ "modified": "2019-07-26 12:17:52.142186",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Pick Ticket Reference Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.py b/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.py
new file mode 100644
index 0000000..412be75
--- /dev/null
+++ b/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class PickTicketReferenceItem(Document):
+	pass