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