From 32371c87553e10cbac4c503bae33aa2f37f32260 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 12 Jul 2019 11:56:42 +0530 Subject: [PATCH 01/79] feat: Init Pick Ticket and Pick Ticket Item --- erpnext/stock/doctype/pick_ticket/__init__.py | 0 .../stock/doctype/pick_ticket/pick_ticket.js | 8 ++ .../doctype/pick_ticket/pick_ticket.json | 79 +++++++++++++++++++ .../stock/doctype/pick_ticket/pick_ticket.py | 10 +++ .../doctype/pick_ticket_item/__init__.py | 0 .../pick_ticket_item/pick_ticket_item.json | 72 +++++++++++++++++ .../pick_ticket_item/pick_ticket_item.py | 10 +++ 7 files changed, 179 insertions(+) create mode 100644 erpnext/stock/doctype/pick_ticket/__init__.py create mode 100644 erpnext/stock/doctype/pick_ticket/pick_ticket.js create mode 100644 erpnext/stock/doctype/pick_ticket/pick_ticket.json create mode 100644 erpnext/stock/doctype/pick_ticket/pick_ticket.py create mode 100644 erpnext/stock/doctype/pick_ticket_item/__init__.py create mode 100644 erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json create mode 100644 erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.py diff --git a/erpnext/stock/doctype/pick_ticket/__init__.py b/erpnext/stock/doctype/pick_ticket/__init__.py new file mode 100644 index 0000000000..e69de29bb2 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 0000000000..f9ae38a3e2 --- /dev/null +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.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', { + // onload: function(frm) { + + // } +}); 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 0000000000..28aa6997cb --- /dev/null +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.json @@ -0,0 +1,79 @@ +{ + "creation": "2019-07-11 16:03:13.681045", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "reference_doctype", + "reference_name", + "company", + "column_break_4", + "group_warehouse", + "section_break_6", + "items" + ], + "fields": [ + { + "fieldname": "reference_doctype", + "fieldtype": "Select", + "label": "Reference Document Type", + "options": "Sales Order\nWork Order" + }, + { + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "label": "Reference Name", + "options": "reference_doctype" + }, + { + "fieldname": "items", + "fieldtype": "Table", + "label": "Items", + "options": "Pick Ticket Item" + }, + { + "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" + } + ], + "modified": "2019-07-12 11:42:03.508514", + "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 0000000000..7f2e89bb6c --- /dev/null +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.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 PickTicket(Document): + 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 0000000000..e69de29bb2 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 0000000000..705172553e --- /dev/null +++ b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json @@ -0,0 +1,72 @@ +{ + "creation": "2019-07-11 16:01:22.832885", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item", + "item_name", + "description", + "reference_document_item", + "warehouse", + "qty", + "picked_qty" + ], + "fields": [ + { + "fieldname": "item", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item", + "options": "Item" + }, + { + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Qty" + }, + { + "fieldname": "picked_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Picked Qty" + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Warehouse", + "options": "Warehouse" + }, + { + "fetch_from": "item.item_name", + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name" + }, + { + "fetch_from": "item.description", + "fieldname": "description", + "fieldtype": "Text", + "label": "Description" + }, + { + "fieldname": "reference_document_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Reference Document Item" + } + ], + "istable": 1, + "modified": "2019-07-11 16:34:52.853146", + "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 0000000000..a13666a438 --- /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 From 778b4926bf7852bb04cc9843a1d08bbec271ae31 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 24 Jul 2019 11:27:32 +0530 Subject: [PATCH 02/79] fix: Fetch warehouse location from Bin --- .../doctype/sales_order/sales_order.js | 8 ++ .../doctype/sales_order/sales_order.py | 77 +++++++++++++++++++ .../pick_ticket_item/pick_ticket_item.json | 37 ++++++++- 3 files changed, 120 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 39dda92e3e..89739cc633 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -102,6 +102,7 @@ frappe.ui.form.on("Sales Order Item", { 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 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( 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 09dc9a9932..181ac33782 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -996,3 +996,80 @@ def make_raw_material_request(items, company, sales_order, project=None): 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, offset=None): + + doc = get_mapped_doc("Sales Order", source_name, { + "Sales Order": { + "doctype": "Pick Ticket", + "validation": { + "docstatus": ["=", 1] + }, + "field_map": { + "name": "reference_name", + }, + }, + "Sales Order Item": { + "doctype": 'Pick Ticket Item', + "field_map": { + 'item_code': 'item' + }, + } + }, target_doc, postprocess) + + return doc + +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) + if not item_locations: return + + remaining_qty = item_doc.qty + + while remaining_qty > 0 and item_locations: + item_location = item_locations.pop(0) + qty = item_doc.qty if item_location.qty >= item_doc.qty else item_location.qty + items.append({ + 'qty': qty, + 'warehouse': item_location.warehouse + }) + remaining_qty -= qty + + return items + +def postprocess(source, doc): + for item in doc.items: + data = get_items_with_warehouse_and_quantity(item) + item.delete() + + for item_info in data: + print(item_info) + pick_item = frappe.new_doc('Pick Ticket Item', doc, 'items') + pick_item.update(item_info) + print(pick_item.qty) + + for item in doc.items: + if item.has_serial_no: + serial_nos = frappe.get_all('Serial No', { + 'item_code': item.item, + 'warehouse': item.warehouse + }, limit=item.qty, order_by='purchase_date') + item.serial_no = '\n'.join([serial_no.name for serial_no in serial_nos]) + + if item.has_batch_no: + serial_nos = frappe.get_all('Serial No', { + 'item_code': item.item, + 'warehouse': item.warehouse + }, limit=item.qty, order_by='purchase_date') + item.serial_no = '\n'.join([serial_no.name for serial_no in serial_nos]) \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json index 705172553e..cba0743c8a 100644 --- a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json +++ b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json @@ -10,7 +10,11 @@ "reference_document_item", "warehouse", "qty", - "picked_qty" + "picked_qty", + "has_serial_no", + "has_batch_no", + "serial_no", + "batch_no" ], "fields": [ { @@ -56,10 +60,39 @@ "fieldtype": "Data", "hidden": 1, "label": "Reference Document Item" + }, + { + "depends_on": "has_serial_no", + "fieldname": "serial_no", + "fieldtype": "Small Text", + "label": "Serial No" + }, + { + "depends_on": "has_batch_no", + "fieldname": "batch_no", + "fieldtype": "Link", + "label": "Batch No", + "options": "Batch" + }, + { + "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 } ], "istable": 1, - "modified": "2019-07-11 16:34:52.853146", + "modified": "2019-07-24 11:05:27.407791", "modified_by": "Administrator", "module": "Stock", "name": "Pick Ticket Item", From 0576ad5ef997c9b92ae110b2fca4e012d09d4712 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 24 Jul 2019 15:16:03 +0530 Subject: [PATCH 03/79] fix: Move pick list creation code to picklist.py file --- .../doctype/sales_order/sales_order.py | 76 +------------------ .../doctype/pick_ticket/pick_ticket.json | 17 +---- .../stock/doctype/pick_ticket/pick_ticket.py | 67 +++++++++++++++- .../pick_ticket_item/pick_ticket_item.json | 46 +++++++++-- 4 files changed, 109 insertions(+), 97 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 181ac33782..1f889ab58c 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -999,77 +999,5 @@ def make_inter_company_purchase_order(source_name, target_doc=None): @frappe.whitelist() def make_pick_ticket(source_name, target_doc=None, offset=None): - - doc = get_mapped_doc("Sales Order", source_name, { - "Sales Order": { - "doctype": "Pick Ticket", - "validation": { - "docstatus": ["=", 1] - }, - "field_map": { - "name": "reference_name", - }, - }, - "Sales Order Item": { - "doctype": 'Pick Ticket Item', - "field_map": { - 'item_code': 'item' - }, - } - }, target_doc, postprocess) - - return doc - -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) - if not item_locations: return - - remaining_qty = item_doc.qty - - while remaining_qty > 0 and item_locations: - item_location = item_locations.pop(0) - qty = item_doc.qty if item_location.qty >= item_doc.qty else item_location.qty - items.append({ - 'qty': qty, - 'warehouse': item_location.warehouse - }) - remaining_qty -= qty - - return items - -def postprocess(source, doc): - for item in doc.items: - data = get_items_with_warehouse_and_quantity(item) - item.delete() - - for item_info in data: - print(item_info) - pick_item = frappe.new_doc('Pick Ticket Item', doc, 'items') - pick_item.update(item_info) - print(pick_item.qty) - - for item in doc.items: - if item.has_serial_no: - serial_nos = frappe.get_all('Serial No', { - 'item_code': item.item, - 'warehouse': item.warehouse - }, limit=item.qty, order_by='purchase_date') - item.serial_no = '\n'.join([serial_no.name for serial_no in serial_nos]) - - if item.has_batch_no: - serial_nos = frappe.get_all('Serial No', { - 'item_code': item.item, - 'warehouse': item.warehouse - }, limit=item.qty, order_by='purchase_date') - item.serial_no = '\n'.join([serial_no.name for serial_no in serial_nos]) \ No newline at end of file + from erpnext.stock.doctype.pick_ticket.pick_ticket import get_pick_list + return get_pick_list('Sales Order', source_name, 'items') diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.json b/erpnext/stock/doctype/pick_ticket/pick_ticket.json index 28aa6997cb..392786f030 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.json +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.json @@ -1,11 +1,10 @@ { + "autoname": "PICK.####", "creation": "2019-07-11 16:03:13.681045", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "reference_doctype", - "reference_name", "company", "column_break_4", "group_warehouse", @@ -13,18 +12,6 @@ "items" ], "fields": [ - { - "fieldname": "reference_doctype", - "fieldtype": "Select", - "label": "Reference Document Type", - "options": "Sales Order\nWork Order" - }, - { - "fieldname": "reference_name", - "fieldtype": "Dynamic Link", - "label": "Reference Name", - "options": "reference_doctype" - }, { "fieldname": "items", "fieldtype": "Table", @@ -53,7 +40,7 @@ "fieldtype": "Section Break" } ], - "modified": "2019-07-12 11:42:03.508514", + "modified": "2019-07-24 14:59:44.542987", "modified_by": "Administrator", "module": "Stock", "name": "Pick Ticket", diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.py b/erpnext/stock/doctype/pick_ticket/pick_ticket.py index 7f2e89bb6c..8982688451 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.py +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.py @@ -3,8 +3,73 @@ # For license information, please see license.txt from __future__ import unicode_literals -# import frappe +import frappe from frappe.model.document import Document class PickTicket(Document): pass + +def get_pick_list(reference_doctype, reference_name, items_field): + doc = frappe.new_doc('Pick Ticket') + reference_doc = frappe.get_doc(reference_doctype, reference_name) + doc.company = reference_doc.company + items = reference_doc.get(items_field) + + add_picklist_items(items, doc, reference_doc) + + doc.insert() + + return doc + +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, reference_doc): + items = [] + item_locations = get_available_items(item_doc.item_code) + if not item_locations: return items + + 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_code, + 'qty': qty, + 'warehouse': item_location.warehouse, + 'reference_doctype': reference_doc.doctype, + 'reference_name': reference_doc.name + }) + remaining_qty -= qty + + return items + +def add_picklist_items(reference_items, doc, reference_doc): + for item in reference_items: + data = get_items_with_warehouse_and_quantity(item, reference_doc) + + for item_info in data: + doc.append('items', item_info) + + for item in doc.get('items'): + if item.has_serial_no: + serial_nos = frappe.get_all('Serial No', { + 'item_code': item.item, + 'warehouse': item.warehouse + }, limit=item.qty, order_by='purchase_date') + item.serial_no = '\n'.join([serial_no.name for serial_no in serial_nos]) + + # if item.has_batch_no: + # serial_nos = frappe.get_all('Batch', { + # 'item_code': item.item, + # 'warehouse': item.warehouse + # }, limit=item.qty, order_by='purchase_date') + # item.serial_no = '\n'.join([serial_no.name for serial_no in serial_nos]) \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json index cba0743c8a..33a42737b1 100644 --- a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json +++ b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json @@ -6,15 +6,20 @@ "field_order": [ "item", "item_name", + "column_break_2", "description", - "reference_document_item", + "has_batch_no", + "has_serial_no", + "section_break_5", "warehouse", "qty", "picked_qty", - "has_serial_no", - "has_batch_no", "serial_no", - "batch_no" + "batch_no", + "reference_section", + "reference_doctype", + "reference_name", + "reference_document_item" ], "fields": [ { @@ -47,13 +52,15 @@ "fetch_from": "item.item_name", "fieldname": "item_name", "fieldtype": "Data", - "label": "Item Name" + "label": "Item Name", + "read_only": 1 }, { "fetch_from": "item.description", "fieldname": "description", "fieldtype": "Text", - "label": "Description" + "label": "Description", + "read_only": 1 }, { "fieldname": "reference_document_item", @@ -89,10 +96,35 @@ "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" + }, + { + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "label": "Reference Document", + "options": "reference_doctype" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" } ], "istable": 1, - "modified": "2019-07-24 11:05:27.407791", + "modified": "2019-07-24 15:09:35.712289", "modified_by": "Administrator", "module": "Stock", "name": "Pick Ticket Item", From 3b318005333472449fdab8eff6cf34d4bc2d8252 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 25 Jul 2019 11:22:45 +0530 Subject: [PATCH 04/79] fix: Add logic to set batch no [WIP] - Add pick ticket reference item --- .../doctype/pick_ticket/pick_ticket.json | 21 +++++++-- .../stock/doctype/pick_ticket/pick_ticket.py | 46 ++++++++++++------- .../pick_ticket_item/pick_ticket_item.json | 24 ++++++---- .../pick_ticket_reference_item.js | 8 ++++ .../pick_ticket_reference_item.json | 27 +++++++++++ .../pick_ticket_reference_item.py | 10 ++++ 6 files changed, 107 insertions(+), 29 deletions(-) create mode 100644 erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.js create mode 100644 erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.json create mode 100644 erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.py diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.json b/erpnext/stock/doctype/pick_ticket/pick_ticket.json index 392786f030..fe331ac3c4 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.json +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.json @@ -8,6 +8,8 @@ "company", "column_break_4", "group_warehouse", + "section_break_4", + "reference_document_items", "section_break_6", "items" ], @@ -15,8 +17,9 @@ { "fieldname": "items", "fieldtype": "Table", - "label": "Items", - "options": "Pick Ticket Item" + "label": "Items Locations", + "options": "Pick Ticket Item", + "read_only": 1 }, { "description": "Items under this warehouse will be suggested", @@ -38,9 +41,21 @@ { "fieldname": "section_break_6", "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "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-24 14:59:44.542987", + "modified": "2019-07-24 16:13:51.668880", "modified_by": "Administrator", "module": "Stock", "name": "Pick Ticket", diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.py b/erpnext/stock/doctype/pick_ticket/pick_ticket.py index 8982688451..4c5fdb8297 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.py +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.py @@ -14,11 +14,8 @@ def get_pick_list(reference_doctype, reference_name, items_field): reference_doc = frappe.get_doc(reference_doctype, reference_name) doc.company = reference_doc.company items = reference_doc.get(items_field) - add_picklist_items(items, doc, reference_doc) - - doc.insert() - + doc.save() return doc def get_available_items(item): @@ -34,10 +31,12 @@ def get_available_items(item): def get_items_with_warehouse_and_quantity(item_doc, reference_doc): items = [] item_locations = get_available_items(item_doc.item_code) - if not item_locations: return items - remaining_qty = item_doc.qty + if not item_locations: + print('{} qty of {} is out of stock. Skipping...'.format(remaining_qty, item_doc.item)) + return items + 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 @@ -59,17 +58,30 @@ def add_picklist_items(reference_items, doc, reference_doc): for item_info in data: doc.append('items', item_info) + doc.insert() + for item in doc.get('items'): if item.has_serial_no: - serial_nos = frappe.get_all('Serial No', { - 'item_code': item.item, - 'warehouse': item.warehouse - }, limit=item.qty, order_by='purchase_date') - item.serial_no = '\n'.join([serial_no.name for serial_no in serial_nos]) + set_serial_nos(item) + elif item.has_batch_no: + set_batch_no(item, doc) - # if item.has_batch_no: - # serial_nos = frappe.get_all('Batch', { - # 'item_code': item.item, - # 'warehouse': item.warehouse - # }, limit=item.qty, order_by='purchase_date') - # item.serial_no = '\n'.join([serial_no.name for serial_no in serial_nos]) \ No newline at end of file +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.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_item/pick_ticket_item.json b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json index 33a42737b1..6db32845e7 100644 --- a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json +++ b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json @@ -33,7 +33,8 @@ "fieldname": "qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Qty" + "label": "Qty", + "read_only": 1 }, { "fieldname": "picked_qty", @@ -46,7 +47,8 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "read_only": 1 }, { "fetch_from": "item.item_name", @@ -66,20 +68,22 @@ "fieldname": "reference_document_item", "fieldtype": "Data", "hidden": 1, - "label": "Reference Document Item" + "label": "Reference Document Item", + "read_only": 1 }, { "depends_on": "has_serial_no", "fieldname": "serial_no", "fieldtype": "Small Text", - "label": "Serial No" + "label": "Serial No", + "read_only": 1 }, { - "depends_on": "has_batch_no", "fieldname": "batch_no", "fieldtype": "Link", "label": "Batch No", - "options": "Batch" + "options": "Batch", + "read_only": 1 }, { "default": "0", @@ -106,13 +110,15 @@ "fieldname": "reference_doctype", "fieldtype": "Select", "label": "Reference Document Type", - "options": "Sales Order\nWork Order" + "options": "Sales Order\nWork Order", + "read_only": 1 }, { "fieldname": "reference_name", "fieldtype": "Dynamic Link", "label": "Reference Document", - "options": "reference_doctype" + "options": "reference_doctype", + "read_only": 1 }, { "fieldname": "column_break_2", @@ -124,7 +130,7 @@ } ], "istable": 1, - "modified": "2019-07-24 15:09:35.712289", + "modified": "2019-07-25 11:18:58.478250", "modified_by": "Administrator", "module": "Stock", "name": "Pick Ticket Item", 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 0000000000..a3f909659b --- /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 0000000000..446e075686 --- /dev/null +++ b/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.json @@ -0,0 +1,27 @@ +{ + "creation": "2019-07-24 16:11:07.415562", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item" + ], + "fields": [ + { + "fieldname": "item", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item", + "options": "Item" + } + ], + "istable": 1, + "modified": "2019-07-24 16:12:58.000378", + "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 0000000000..412be75f88 --- /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 From 64f174fcaadc186e9bd9cddad96a007ac1690de8 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 26 Jul 2019 13:22:05 +0530 Subject: [PATCH 05/79] feat: Get item location from reference items - Add option to get items from multiple sales order - Add some required fiels to Pick TIcket Reference Item --- .../doctype/sales_order/sales_order.py | 22 ++++++-- .../stock/doctype/pick_ticket/pick_ticket.js | 25 ++++++++- .../doctype/pick_ticket/pick_ticket.json | 3 +- .../stock/doctype/pick_ticket/pick_ticket.py | 54 ++++++++----------- .../pick_ticket_reference_item.json | 25 ++++++++- 5 files changed, 88 insertions(+), 41 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 1f889ab58c..97a9739e79 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -998,6 +998,22 @@ def make_inter_company_purchase_order(source_name, target_doc=None): return make_inter_company_transaction("Sales Order", source_name, target_doc) @frappe.whitelist() -def make_pick_ticket(source_name, target_doc=None, offset=None): - from erpnext.stock.doctype.pick_ticket.pick_ticket import get_pick_list - return get_pick_list('Sales Order', source_name, 'items') +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/pick_ticket.js b/erpnext/stock/doctype/pick_ticket/pick_ticket.js index f9ae38a3e2..a2d6cd71de 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.js +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.js @@ -2,7 +2,28 @@ // For license information, please see license.txt frappe.ui.form.on('Pick Ticket', { - // onload: function(frm) { + 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 index fe331ac3c4..935192568e 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.json +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.json @@ -43,7 +43,6 @@ "fieldtype": "Section Break" }, { - "collapsible": 1, "fieldname": "section_break_4", "fieldtype": "Section Break", "label": "Reference Items" @@ -55,7 +54,7 @@ "options": "Pick Ticket Reference Item" } ], - "modified": "2019-07-24 16:13:51.668880", + "modified": "2019-07-26 12:06:08.941760", "modified_by": "Administrator", "module": "Stock", "name": "Pick Ticket", diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.py b/erpnext/stock/doctype/pick_ticket/pick_ticket.py index 4c5fdb8297..eb54730616 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.py +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.py @@ -7,16 +7,20 @@ import frappe from frappe.model.document import Document class PickTicket(Document): - pass + 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) -def get_pick_list(reference_doctype, reference_name, items_field): - doc = frappe.new_doc('Pick Ticket') - reference_doc = frappe.get_doc(reference_doctype, reference_name) - doc.company = reference_doc.company - items = reference_doc.get(items_field) - add_picklist_items(items, doc, reference_doc) - doc.save() - return doc + 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 @@ -28,44 +32,30 @@ def get_available_items(item): return available_items -def get_items_with_warehouse_and_quantity(item_doc, reference_doc): +def get_items_with_warehouse_and_quantity(item_doc): items = [] - item_locations = get_available_items(item_doc.item_code) + item_locations = get_available_items(item_doc.item) remaining_qty = item_doc.qty - if not item_locations: - print('{} qty of {} is out of stock. Skipping...'.format(remaining_qty, item_doc.item)) - return items 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_code, + 'item': item_doc.item, 'qty': qty, 'warehouse': item_location.warehouse, - 'reference_doctype': reference_doc.doctype, - 'reference_name': reference_doc.name + '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 add_picklist_items(reference_items, doc, reference_doc): - for item in reference_items: - data = get_items_with_warehouse_and_quantity(item, reference_doc) - - for item_info in data: - doc.append('items', item_info) - - doc.insert() - - for item in doc.get('items'): - if item.has_serial_no: - set_serial_nos(item) - elif item.has_batch_no: - set_batch_no(item, doc) - def set_serial_nos(item): serial_nos = frappe.get_all('Serial No', { 'item_code': item.item, 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 index 446e075686..ae7ea3567e 100644 --- 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 @@ -4,7 +4,10 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "item" + "item", + "qty", + "reference_doctype", + "reference_name" ], "fields": [ { @@ -13,10 +16,28 @@ "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-24 16:12:58.000378", + "modified": "2019-07-26 12:17:52.142186", "modified_by": "Administrator", "module": "Stock", "name": "Pick Ticket Reference Item", From d43764c8e595d86376131333235177043d434a3c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 28 Jul 2019 22:03:28 +0530 Subject: [PATCH 06/79] fix: Use .set method to set serial no. --- erpnext/stock/doctype/pick_ticket/pick_ticket.py | 2 +- erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.py b/erpnext/stock/doctype/pick_ticket/pick_ticket.py index eb54730616..18382e6781 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.py +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.py @@ -61,7 +61,7 @@ def set_serial_nos(item): 'item_code': item.item, 'warehouse': item.warehouse }, limit=item.qty, order_by='purchase_date') - item.serial_no = '\n'.join([serial_no.name for serial_no in serial_nos]) + 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', diff --git a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json index 6db32845e7..7095be68f0 100644 --- a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json +++ b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json @@ -72,7 +72,6 @@ "read_only": 1 }, { - "depends_on": "has_serial_no", "fieldname": "serial_no", "fieldtype": "Small Text", "label": "Serial No", @@ -130,7 +129,7 @@ } ], "istable": 1, - "modified": "2019-07-25 11:18:58.478250", + "modified": "2019-07-26 14:47:33.965373", "modified_by": "Administrator", "module": "Stock", "name": "Pick Ticket Item", From 27bdba49d4ff93fac0ddbda72f7b639db4cdffeb Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 28 Jul 2019 22:04:07 +0530 Subject: [PATCH 07/79] test: Add test case statements --- .../doctype/pick_ticket/test_pick_ticket.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 erpnext/stock/doctype/pick_ticket/test_pick_ticket.py 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 0000000000..3fee6db667 --- /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 From 9ad10cb425732be855ac390480de4327e2e32182 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 29 Jul 2019 16:52:26 +0530 Subject: [PATCH 08/79] test: Add basic tests --- .../doctype/pick_ticket/test_pick_ticket.py | 105 ++++++++++++++++-- 1 file changed, 96 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/pick_ticket/test_pick_ticket.py b/erpnext/stock/doctype/pick_ticket/test_pick_ticket.py index 3fee6db667..3a0492a2dc 100644 --- a/erpnext/stock/doctype/pick_ticket/test_pick_ticket.py +++ b/erpnext/stock/doctype/pick_ticket/test_pick_ticket.py @@ -3,21 +3,108 @@ # See license.txt from __future__ import unicode_literals -# import frappe +import frappe import unittest +# test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch'] + +from erpnext.selling.doctype.sales_order.sales_order import make_pick_ticket class TestPickTicket(unittest.TestCase): - def test_pick_list_picks_warehouse_for_each_item(): + def test_pick_ticket_picks_warehouse_for_each_item(self): + pick_ticket = frappe.get_doc({ + 'doctype': 'Pick Ticket', + 'company': '_Test Company', + 'reference_document_items': [{ + 'item': '_Test Item Home Desktop 100', + 'reference_doctype': 'Sales Order', + 'qty': 5, + 'reference_name': '_T-Sales Order-1', + }], + }) + + pick_ticket.set_item_locations() + + self.assertEqual(pick_ticket.items[0].item, '_Test Item Home Desktop 100') + self.assertEqual(pick_ticket.items[0].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_ticket.items[0].qty, 5) + + def test_pick_ticket_skips_out_of_stock_item(self): + pick_ticket = frappe.get_doc({ + 'doctype': 'Pick Ticket', + 'company': '_Test Company', + 'reference_document_items': [{ + 'item': '_Test Item Warehouse Group Wise Reorder', + 'reference_doctype': 'Sales Order', + 'qty': 1000, + 'reference_name': '_T-Sales Order-1', + }], + }) + + pick_ticket.set_item_locations() + + self.assertEqual(pick_ticket.items[0].item, '_Test Item Warehouse Group Wise Reorder') + self.assertEqual(pick_ticket.items[0].warehouse, '_Test Warehouse Group-C1 - _TC') + self.assertEqual(pick_ticket.items[0].qty, 30) + + + def test_pick_ticket_skips_items_in_expired_batch(self): pass - def test_pick_list_skips_out_of_warranty_item(): + def test_pick_ticket_shows_serial_no_for_serialized_item(self): + + stock_reconciliation = frappe.get_doc({ + 'doctype': 'Stock Reconciliation', + 'company': '_Test Company', + 'items': [{ + 'item_code': '_Test Serialized Item', + 'warehouse': '_Test Warehouse - _TC', + 'qty': 5, + 'serial_no': '123450\n123451\n123452\n123453\n123454' + }] + }) + + stock_reconciliation.submit() + + pick_ticket = frappe.get_doc({ + 'doctype': 'Pick Ticket', + 'company': '_Test Company', + 'reference_document_items': [{ + 'item': '_Test Serialized Item', + 'reference_doctype': 'Sales Order', + 'qty': 1000, + 'reference_name': '_T-Sales Order-1', + }], + }) + + pick_ticket.set_item_locations() + self.assertEqual(pick_ticket.items[0].item, '_Test Serialized Item') + self.assertEqual(pick_ticket.items[0].warehouse, '_Test Warehouse Group-C1 - _TC') + self.assertEqual(pick_ticket.items[0].qty, 30) + self.assertEqual(pick_ticket.items[0].serial_no, 30) + + + def test_pick_ticket_for_multiple_reference_doctypes(self): pass - def test_pick_list_skips_items_in_expired_batch(): - pass - def test_pick_list_shows_serial_no_for_serialized_item(): - pass +# def create_new_pick_ticket(): +# pass +# doc = frappe.new_doc('Pick Ticket') +# doc.items.append({ +# 'item': '_Test Warehouse - _TC', +# '' +# }) - def test_pick_list_for_multiple_reference_doctypes(): - pass + + +## records required + +''' +batch no +items +sales invoice +stock entries + bin + stock ledger entry +warehouses +''' \ No newline at end of file From 55ab31314c6ef69a65e74e43e08977af5239aab9 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 30 Jul 2019 09:02:03 +0530 Subject: [PATCH 09/79] fix: Add code to set batch no - And split item based on batch availibility --- .../stock/doctype/pick_ticket/pick_ticket.py | 80 ++++++++++++++----- 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.py b/erpnext/stock/doctype/pick_ticket/pick_ticket.py index 18382e6781..93eb7134a1 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.py +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.py @@ -14,13 +14,13 @@ class PickTicket(Document): data = get_items_with_warehouse_and_quantity(item) for item_info in data: - self.append('items', item_info) + print(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) + for item_doc in self.get('items'): + if frappe.get_cached_value('Item', item_doc.item, 'has_serial_no'): + set_serial_nos(item_doc) + elif frappe.get_cached_value('Item', item_doc.item, 'has_batch_no'): + set_batch_no(item_doc, self) def get_available_items(item): # gets all items available in different warehouses @@ -56,22 +56,58 @@ def get_items_with_warehouse_and_quantity(item_doc): return items -def set_serial_nos(item): +def set_serial_nos(item_doc): 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])) + 'item_code': item_doc.item, + 'warehouse': item_doc.warehouse + }, limit=item_doc.qty, order_by='purchase_date') + item_doc.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') +def set_batch_no(item_doc, parent_doc): + batches = frappe.db.sql(""" + SELECT + `batch_no`, + SUM(`actual_qty`) AS `qty` + FROM + `tabStock Ledger Entry` + WHERE + `item_code`=%(item_code)s + AND `warehouse`=%(warehouse)s + GROUP BY + `warehouse`, + `batch_no`, + `item_code` + HAVING `qty` > 0 + """, { + 'item_code': item_doc.item, + 'warehouse': item_doc.warehouse, + }, as_dict=1) + print(batches) - 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 + required_qty = item_doc.qty + while required_qty > 0 and batches: + batch = batches.pop() + batch_expiry = frappe.get_value('Batch', batch.batch_no, 'expiry_date') + if batch_expiry and batch_expiry < frappe.utils.getdate(): + print('---------- Batch {} is expired. Skipping... -------------'.format(batch.batch_no)) + continue + item_doc.batch_no = batch.batch_no + required_qty -= batch.qty + if batch.qty >= item_doc.qty: + break + else: + # split item if quantity of item in batch is less that required + # Look for another batch + + # set quantity of of item equal to batch quantity + item_doc.set('qty', batch.qty) + item_doc = parent_doc.append('items', { + 'item': item_doc.item, + 'qty': required_qty, + 'warehouse': item_doc.warehouse, + 'reference_doctype': item_doc.reference_doctype, + 'reference_name': item_doc.reference_name + }) + if required_qty: + print('---------- No batches found for {} qty of {}. Skipping... -------------'.format(required_qty, item_doc.item)) + parent_doc.remove(item_doc) From e260e8239fd183c14d6403ff28e14e287ce94606 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 30 Jul 2019 10:02:10 +0530 Subject: [PATCH 10/79] fix: Batch number selection code --- erpnext/stock/doctype/pick_ticket/pick_ticket.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.py b/erpnext/stock/doctype/pick_ticket/pick_ticket.py index 93eb7134a1..ce0e5526b6 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.py +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.py @@ -63,6 +63,8 @@ def set_serial_nos(item_doc): }, limit=item_doc.qty, order_by='purchase_date') item_doc.set('serial_no', '\n'.join([serial_no.name for serial_no in serial_nos])) + # should we assume that all serialized item available in stock will have serial no? + def set_batch_no(item_doc, parent_doc): batches = frappe.db.sql(""" SELECT @@ -88,18 +90,19 @@ def set_batch_no(item_doc, parent_doc): while required_qty > 0 and batches: batch = batches.pop() batch_expiry = frappe.get_value('Batch', batch.batch_no, 'expiry_date') - if batch_expiry and batch_expiry < frappe.utils.getdate(): + if batch_expiry and batch_expiry <= frappe.utils.getdate(): print('---------- Batch {} is expired. Skipping... -------------'.format(batch.batch_no)) continue item_doc.batch_no = batch.batch_no - required_qty -= batch.qty if batch.qty >= item_doc.qty: + required_qty = 0 break else: # split item if quantity of item in batch is less that required # Look for another batch # set quantity of of item equal to batch quantity + required_qty -= batch.qty item_doc.set('qty', batch.qty) item_doc = parent_doc.append('items', { 'item': item_doc.item, From 341f7733d1e2ce8b0bd1c5a41e957a48af3735f4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 30 Jul 2019 10:02:50 +0530 Subject: [PATCH 11/79] fix: Make item read-only in item location table --- erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json index 7095be68f0..0a65eed376 100644 --- a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json +++ b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json @@ -27,7 +27,8 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Item", - "options": "Item" + "options": "Item", + "read_only": 1 }, { "fieldname": "qty", @@ -129,7 +130,7 @@ } ], "istable": 1, - "modified": "2019-07-26 14:47:33.965373", + "modified": "2019-07-30 09:28:44.969479", "modified_by": "Administrator", "module": "Stock", "name": "Pick Ticket Item", From 14fb1600ec22b0ee3d78c5c49740615db215e016 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 30 Jul 2019 11:47:21 +0530 Subject: [PATCH 12/79] fix: Select location based on group warehouse --- .../stock/doctype/pick_ticket/pick_ticket.js | 10 ++++ .../stock/doctype/pick_ticket/pick_ticket.py | 47 ++++++++++++------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.js b/erpnext/stock/doctype/pick_ticket/pick_ticket.js index a2d6cd71de..87fad7f9ee 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.js +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.js @@ -2,6 +2,16 @@ // For license information, please see license.txt frappe.ui.form.on('Pick Ticket', { + setup: (frm) => { + frm.set_query('group_warehouse', () => { + return { + filters: { + 'is_group': 1, + 'company': frm.doc.company + } + }; + }); + }, refresh: (frm) => { this.frm.add_custom_button(__('Sales Order'), function() { erpnext.utils.map_current_doc({ diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.py b/erpnext/stock/doctype/pick_ticket/pick_ticket.py index ce0e5526b6..a1adf9fee4 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.py +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.py @@ -9,10 +9,15 @@ from frappe.model.document import Document class PickTicket(Document): def set_item_locations(self): reference_items = self.reference_document_items + + from_warehouses = None + if self.group_warehouse: + from_warehouses = frappe.db.get_descendants('Warehouse', self.group_warehouse) + + # Reset self.delete_key('items') for item in reference_items: - data = get_items_with_warehouse_and_quantity(item) - + data = get_items_with_warehouse_and_quantity(item, from_warehouses) for item_info in data: print(self.append('items', item_info)) @@ -22,22 +27,11 @@ class PickTicket(Document): elif frappe.get_cached_value('Item', item_doc.item, 'has_batch_no'): set_batch_no(item_doc, 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): +def get_items_with_warehouse_and_quantity(item_doc, from_warehouses): items = [] - item_locations = get_available_items(item_doc.item) + item_locations = get_available_items(item_doc.item, from_warehouses) 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 @@ -51,11 +45,28 @@ def get_items_with_warehouse_and_quantity(item_doc): remaining_qty -= qty if remaining_qty: - print('---------- {} qty of {} is out of stock. Skipping... -------------'.format(remaining_qty, item_doc.item)) + frappe.msgprint('{} qty of {} is out of stock. Skipping...'.format(remaining_qty, item_doc.item)) return items return items +def get_available_items(item, from_warehouses): + # gets all items available in different warehouses + # FIFO + filters = frappe._dict({ + 'item_code': item, + 'actual_qty': ['>', 0] + }) + if from_warehouses: + filters.warehouse = ['in', from_warehouses] + + available_items = frappe.get_all('Bin', + fields=['warehouse', 'actual_qty as qty'], + filters=filters, + order_by='creation') + + return available_items + def set_serial_nos(item_doc): serial_nos = frappe.get_all('Serial No', { 'item_code': item_doc.item, @@ -91,7 +102,7 @@ def set_batch_no(item_doc, parent_doc): batch = batches.pop() batch_expiry = frappe.get_value('Batch', batch.batch_no, 'expiry_date') if batch_expiry and batch_expiry <= frappe.utils.getdate(): - print('---------- Batch {} is expired. Skipping... -------------'.format(batch.batch_no)) + frappe.msgprint('Skipping expired Batch {}'.format(batch.batch_no)) continue item_doc.batch_no = batch.batch_no if batch.qty >= item_doc.qty: @@ -112,5 +123,5 @@ def set_batch_no(item_doc, parent_doc): 'reference_name': item_doc.reference_name }) if required_qty: - print('---------- No batches found for {} qty of {}. Skipping... -------------'.format(required_qty, item_doc.item)) + frappe.msgprint('No batches found for {} qty of {}. Skipping...'.format(required_qty, item_doc.item)) parent_doc.remove(item_doc) From 72d03464dcc584885738d6e7a634264ad49bbc21 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 30 Jul 2019 11:56:24 +0530 Subject: [PATCH 13/79] fix: Show "Get Item Location" only if there are reference items --- erpnext/stock/doctype/pick_ticket/pick_ticket.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.js b/erpnext/stock/doctype/pick_ticket/pick_ticket.js index 87fad7f9ee..5903e6fea0 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.js +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.js @@ -27,13 +27,11 @@ frappe.ui.form.on('Pick Ticket', { }); }, __("Get items from")); - frm.add_custom_button(__('Get Item Locations'), () => { - frm.trigger('set_item_locations'); - }); + if (frm.doc.reference_document_items.length) { + frm.add_custom_button(__('Get Item Locations'), () => { + frm.call('set_item_locations'); + }); + } }, - set_item_locations: (frm) => { - frm.call('set_item_locations') - } - }); From fa9111ebe276395aeb35a8b73bc490bcde257d4b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 1 Aug 2019 11:07:14 +0530 Subject: [PATCH 14/79] fix: Add and rename some fields - Add ability to create delivery note from pick ticket - Minor fix related to create button --- .../doctype/sales_order/sales_order.js | 5 ++- .../doctype/sales_order/sales_order.py | 3 +- .../stock/doctype/pick_ticket/pick_ticket.js | 14 +++++-- .../doctype/pick_ticket/pick_ticket.json | 42 +++++++++---------- .../stock/doctype/pick_ticket/pick_ticket.py | 39 +++++++++++++---- .../doctype/pick_ticket/test_pick_ticket.py | 36 ++++++---------- .../pick_ticket_item/pick_ticket_item.json | 3 +- .../pick_ticket_reference_item.json | 10 ++++- 8 files changed, 88 insertions(+), 64 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 89739cc633..6e6b730bc9 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -102,7 +102,6 @@ frappe.ui.form.on("Sales Order Item", { 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) { @@ -110,7 +109,9 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( this._super(); let allow_delivery = false; - if(doc.docstatus==1) { + if (doc.docstatus==1) { + this.frm.add_custom_button(__('Pick Ticket'), () => this.make_pick_ticket(), __('Create')); + if(this.frm.has_perm("submit")) { if(doc.status === 'On Hold') { // un-hold diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 97a9739e79..5e9a35883e 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1011,7 +1011,8 @@ def make_pick_ticket(source_name, target_doc=None): "field_map": { "item_code": "item", "parenttype": "reference_doctype", - "parent": "reference_name" + "parent": "reference_name", + "name": "reference_document_item" }, }, }, target_doc) diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.js b/erpnext/stock/doctype/pick_ticket/pick_ticket.js index 5903e6fea0..5e5881ed62 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.js +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.js @@ -3,7 +3,7 @@ frappe.ui.form.on('Pick Ticket', { setup: (frm) => { - frm.set_query('group_warehouse', () => { + frm.set_query('parent_warehouse', () => { return { filters: { 'is_group': 1, @@ -13,7 +13,8 @@ frappe.ui.form.on('Pick Ticket', { }); }, refresh: (frm) => { - this.frm.add_custom_button(__('Sales Order'), function() { + frm.add_custom_button(__('Delivery Note'), () => frm.trigger('make_delivery_note'), __('Create')); + 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", @@ -27,11 +28,16 @@ frappe.ui.form.on('Pick Ticket', { }); }, __("Get items from")); - if (frm.doc.reference_document_items.length) { + if (frm.doc.reference_items && frm.doc.reference_items.length) { frm.add_custom_button(__('Get Item Locations'), () => { frm.call('set_item_locations'); }); } }, - + make_delivery_note(frm) { + frappe.model.open_mapped_doc({ + method: "erpnext.stock.doctype.pick_ticket.pick_ticket.make_delivery_note", + frm: frm + }); + }, }); diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.json b/erpnext/stock/doctype/pick_ticket/pick_ticket.json index 935192568e..d8a8e0175c 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.json +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.json @@ -7,27 +7,13 @@ "field_order": [ "company", "column_break_4", - "group_warehouse", + "parent_warehouse", "section_break_4", - "reference_document_items", + "reference_items", "section_break_6", - "items" + "item_locations" ], "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", @@ -44,17 +30,29 @@ }, { "fieldname": "section_break_4", - "fieldtype": "Section Break", - "label": "Reference Items" + "fieldtype": "Section Break" }, { - "fieldname": "reference_document_items", + "description": "Items under this warehouse will be suggested", + "fieldname": "parent_warehouse", + "fieldtype": "Link", + "label": "Parent Warehouse", + "options": "Warehouse" + }, + { + "fieldname": "item_locations", "fieldtype": "Table", - "label": "Reference Document Items", + "label": "Item Locations", + "options": "Pick Ticket Item" + }, + { + "fieldname": "reference_items", + "fieldtype": "Table", + "label": "Items To Be Picked", "options": "Pick Ticket Reference Item" } ], - "modified": "2019-07-26 12:06:08.941760", + "modified": "2019-08-01 10:50:17.055509", "modified_by": "Administrator", "module": "Stock", "name": "Pick Ticket", diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.py b/erpnext/stock/doctype/pick_ticket/pick_ticket.py index a1adf9fee4..16f6d7a5df 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.py +++ b/erpnext/stock/doctype/pick_ticket/pick_ticket.py @@ -5,23 +5,24 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe.model.mapper import get_mapped_doc class PickTicket(Document): def set_item_locations(self): - reference_items = self.reference_document_items + reference_items = self.reference_items from_warehouses = None - if self.group_warehouse: - from_warehouses = frappe.db.get_descendants('Warehouse', self.group_warehouse) + if self.parent_warehouse: + from_warehouses = frappe.db.get_descendants('Warehouse', self.parent_warehouse) # Reset - self.delete_key('items') + self.delete_key('item_locations') for item in reference_items: data = get_items_with_warehouse_and_quantity(item, from_warehouses) for item_info in data: - print(self.append('items', item_info)) + print(self.append('item_locations', item_info)) - for item_doc in self.get('items'): + for item_doc in self.get('item_locations'): if frappe.get_cached_value('Item', item_doc.item, 'has_serial_no'): set_serial_nos(item_doc) elif frappe.get_cached_value('Item', item_doc.item, 'has_batch_no'): @@ -40,7 +41,8 @@ def get_items_with_warehouse_and_quantity(item_doc, from_warehouses): 'qty': qty, 'warehouse': item_location.warehouse, 'reference_doctype': item_doc.reference_doctype, - 'reference_name': item_doc.reference_name + 'reference_name': item_doc.reference_name, + 'reference_document_item': item_doc.reference_document_item, }) remaining_qty -= qty @@ -120,8 +122,29 @@ def set_batch_no(item_doc, parent_doc): 'qty': required_qty, 'warehouse': item_doc.warehouse, 'reference_doctype': item_doc.reference_doctype, - 'reference_name': item_doc.reference_name + 'reference_name': item_doc.reference_name, + 'reference_document_item': item_doc.reference_document_item, }) if required_qty: frappe.msgprint('No batches found for {} qty of {}. Skipping...'.format(required_qty, item_doc.item)) parent_doc.remove(item_doc) + +@frappe.whitelist() +def make_delivery_note(source_name, target_doc=None): + target_doc = get_mapped_doc("Pick Ticket", source_name, { + "Pick Ticket": { + "doctype": "Delivery Note", + # "validation": { + # "docstatus": ["=", 1] + # } + }, + "Pick Ticket Item": { + "doctype": "Delivery Note Item", + "field_map": { + "item": "item_code", + "reference_docname": "against_sales_order", + }, + }, + }, target_doc) + + return target_doc \ 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 index 3a0492a2dc..5a41bc9f2c 100644 --- a/erpnext/stock/doctype/pick_ticket/test_pick_ticket.py +++ b/erpnext/stock/doctype/pick_ticket/test_pick_ticket.py @@ -14,7 +14,7 @@ class TestPickTicket(unittest.TestCase): pick_ticket = frappe.get_doc({ 'doctype': 'Pick Ticket', 'company': '_Test Company', - 'reference_document_items': [{ + 'reference_items': [{ 'item': '_Test Item Home Desktop 100', 'reference_doctype': 'Sales Order', 'qty': 5, @@ -24,15 +24,15 @@ class TestPickTicket(unittest.TestCase): pick_ticket.set_item_locations() - self.assertEqual(pick_ticket.items[0].item, '_Test Item Home Desktop 100') - self.assertEqual(pick_ticket.items[0].warehouse, '_Test Warehouse - _TC') - self.assertEqual(pick_ticket.items[0].qty, 5) + self.assertEqual(pick_ticket.items_locations[0].item, '_Test Item Home Desktop 100') + self.assertEqual(pick_ticket.items_locations[0].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_ticket.items_locations[0].qty, 5) def test_pick_ticket_skips_out_of_stock_item(self): pick_ticket = frappe.get_doc({ 'doctype': 'Pick Ticket', 'company': '_Test Company', - 'reference_document_items': [{ + 'reference_items': [{ 'item': '_Test Item Warehouse Group Wise Reorder', 'reference_doctype': 'Sales Order', 'qty': 1000, @@ -42,9 +42,9 @@ class TestPickTicket(unittest.TestCase): pick_ticket.set_item_locations() - self.assertEqual(pick_ticket.items[0].item, '_Test Item Warehouse Group Wise Reorder') - self.assertEqual(pick_ticket.items[0].warehouse, '_Test Warehouse Group-C1 - _TC') - self.assertEqual(pick_ticket.items[0].qty, 30) + self.assertEqual(pick_ticket.items_locations[0].item, '_Test Item Warehouse Group Wise Reorder') + self.assertEqual(pick_ticket.items_locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') + self.assertEqual(pick_ticket.items_locations[0].qty, 30) def test_pick_ticket_skips_items_in_expired_batch(self): @@ -68,7 +68,7 @@ class TestPickTicket(unittest.TestCase): pick_ticket = frappe.get_doc({ 'doctype': 'Pick Ticket', 'company': '_Test Company', - 'reference_document_items': [{ + 'reference_items': [{ 'item': '_Test Serialized Item', 'reference_doctype': 'Sales Order', 'qty': 1000, @@ -77,26 +77,16 @@ class TestPickTicket(unittest.TestCase): }) pick_ticket.set_item_locations() - self.assertEqual(pick_ticket.items[0].item, '_Test Serialized Item') - self.assertEqual(pick_ticket.items[0].warehouse, '_Test Warehouse Group-C1 - _TC') - self.assertEqual(pick_ticket.items[0].qty, 30) - self.assertEqual(pick_ticket.items[0].serial_no, 30) + self.assertEqual(pick_ticket.items_locations[0].item, '_Test Serialized Item') + self.assertEqual(pick_ticket.items_locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') + self.assertEqual(pick_ticket.items_locations[0].qty, 30) + self.assertEqual(pick_ticket.items_locations[0].serial_no, 30) def test_pick_ticket_for_multiple_reference_doctypes(self): pass -# def create_new_pick_ticket(): -# pass -# doc = frappe.new_doc('Pick Ticket') -# doc.items.append({ -# 'item': '_Test Warehouse - _TC', -# '' -# }) - - - ## records required ''' diff --git a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json index 0a65eed376..67a7e9caa5 100644 --- a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json +++ b/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json @@ -68,7 +68,6 @@ { "fieldname": "reference_document_item", "fieldtype": "Data", - "hidden": 1, "label": "Reference Document Item", "read_only": 1 }, @@ -130,7 +129,7 @@ } ], "istable": 1, - "modified": "2019-07-30 09:28:44.969479", + "modified": "2019-07-30 23:47:53.566473", "modified_by": "Administrator", "module": "Stock", "name": "Pick Ticket Item", 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 index ae7ea3567e..c31e2bcd23 100644 --- 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 @@ -7,7 +7,8 @@ "item", "qty", "reference_doctype", - "reference_name" + "reference_name", + "reference_document_item" ], "fields": [ { @@ -34,10 +35,15 @@ "fieldtype": "Float", "in_list_view": 1, "label": "Qty" + }, + { + "fieldname": "reference_document_item", + "fieldtype": "Data", + "label": "Reference Document Item" } ], "istable": 1, - "modified": "2019-07-26 12:17:52.142186", + "modified": "2019-07-30 23:43:30.901151", "modified_by": "Administrator", "module": "Stock", "name": "Pick Ticket Reference Item", From fbcc56536b36c72e5d97a66e1b1d22f6bd03a982 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 1 Aug 2019 12:05:20 +0530 Subject: [PATCH 15/79] fix: Rename Pick TIcket -> Pick List --- .../doctype/sales_order/sales_order.js | 2 +- .../doctype/sales_order/sales_order.py | 4 +- .../{pick_ticket => pick_list}/__init__.py | 0 .../pick_ticket.js => pick_list/pick_list.js} | 6 +- .../pick_list.json} | 6 +- .../pick_ticket.py => pick_list/pick_list.py} | 8 +- .../stock/doctype/pick_list/test_pick_list.py | 100 ++++++++++++++++++ .../pick_list_item}/__init__.py | 0 .../pick_list_item}/pick_ticket_item.json | 2 +- .../pick_list_item}/pick_ticket_item.py | 2 +- .../pick_list_reference_item/__init__.py | 0 .../pick_list_reference_item.js} | 2 +- .../pick_list_reference_item.json} | 2 +- .../pick_list_reference_item.py} | 2 +- .../doctype/pick_ticket/test_pick_ticket.py | 100 ------------------ 15 files changed, 118 insertions(+), 118 deletions(-) rename erpnext/stock/doctype/{pick_ticket => pick_list}/__init__.py (100%) rename erpnext/stock/doctype/{pick_ticket/pick_ticket.js => pick_list/pick_list.js} (88%) rename erpnext/stock/doctype/{pick_ticket/pick_ticket.json => pick_list/pick_list.json} (93%) rename erpnext/stock/doctype/{pick_ticket/pick_ticket.py => pick_list/pick_list.py} (97%) create mode 100644 erpnext/stock/doctype/pick_list/test_pick_list.py rename erpnext/stock/doctype/{pick_ticket_item => pick_list_item/pick_list_item}/__init__.py (100%) rename erpnext/stock/doctype/{pick_ticket_item => pick_list_item/pick_list_item}/pick_ticket_item.json (98%) rename erpnext/stock/doctype/{pick_ticket_item => pick_list_item/pick_list_item}/pick_ticket_item.py (88%) create mode 100644 erpnext/stock/doctype/pick_list_reference_item/__init__.py rename erpnext/stock/doctype/{pick_ticket_reference_item/pick_ticket_reference_item.js => pick_list_reference_item/pick_list_reference_item.js} (76%) rename erpnext/stock/doctype/{pick_ticket_reference_item/pick_ticket_reference_item.json => pick_list_reference_item/pick_list_reference_item.json} (96%) rename erpnext/stock/doctype/{pick_ticket_reference_item/pick_ticket_reference_item.py => pick_list_reference_item/pick_list_reference_item.py} (85%) delete mode 100644 erpnext/stock/doctype/pick_ticket/test_pick_ticket.py diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 6e6b730bc9..136ff55344 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -110,7 +110,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( let allow_delivery = false; if (doc.docstatus==1) { - this.frm.add_custom_button(__('Pick Ticket'), () => this.make_pick_ticket(), __('Create')); + this.frm.add_custom_button(__('Pick List'), () => this.make_pick_ticket(), __('Create')); if(this.frm.has_perm("submit")) { if(doc.status === 'On Hold') { diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 5e9a35883e..1d636e3c26 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1001,13 +1001,13 @@ def make_inter_company_purchase_order(source_name, target_doc=None): def make_pick_ticket(source_name, target_doc=None): doc = get_mapped_doc("Sales Order", source_name, { "Sales Order": { - "doctype": "Pick Ticket", + "doctype": "Pick List", "validation": { "docstatus": ["=", 1] } }, "Sales Order Item": { - "doctype": "Pick Ticket Reference Item", + "doctype": "Pick List Reference Item", "field_map": { "item_code": "item", "parenttype": "reference_doctype", diff --git a/erpnext/stock/doctype/pick_ticket/__init__.py b/erpnext/stock/doctype/pick_list/__init__.py similarity index 100% rename from erpnext/stock/doctype/pick_ticket/__init__.py rename to erpnext/stock/doctype/pick_list/__init__.py diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.js b/erpnext/stock/doctype/pick_list/pick_list.js similarity index 88% rename from erpnext/stock/doctype/pick_ticket/pick_ticket.js rename to erpnext/stock/doctype/pick_list/pick_list.js index 5e5881ed62..4cf4cdf78a 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -1,7 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Pick Ticket', { +frappe.ui.form.on('Pick List', { setup: (frm) => { frm.set_query('parent_warehouse', () => { return { @@ -16,7 +16,7 @@ frappe.ui.form.on('Pick Ticket', { frm.add_custom_button(__('Delivery Note'), () => frm.trigger('make_delivery_note'), __('Create')); frm.add_custom_button(__('Sales Order'), function() { erpnext.utils.map_current_doc({ - method: "erpnext.selling.doctype.sales_order.sales_order.make_pick_ticket", + method: "erpnext.selling.doctype.sales_order.sales_order.make_pick_list", source_doctype: "Sales Order", target: frm, setters: { @@ -36,7 +36,7 @@ frappe.ui.form.on('Pick Ticket', { }, make_delivery_note(frm) { frappe.model.open_mapped_doc({ - method: "erpnext.stock.doctype.pick_ticket.pick_ticket.make_delivery_note", + method: "erpnext.stock.doctype.pick_list.pick_list.make_delivery_note", frm: frm }); }, diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.json b/erpnext/stock/doctype/pick_list/pick_list.json similarity index 93% rename from erpnext/stock/doctype/pick_ticket/pick_ticket.json rename to erpnext/stock/doctype/pick_list/pick_list.json index d8a8e0175c..3f96630faf 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -43,19 +43,19 @@ "fieldname": "item_locations", "fieldtype": "Table", "label": "Item Locations", - "options": "Pick Ticket Item" + "options": "Pick List Item" }, { "fieldname": "reference_items", "fieldtype": "Table", "label": "Items To Be Picked", - "options": "Pick Ticket Reference Item" + "options": "Pick List Reference Item" } ], "modified": "2019-08-01 10:50:17.055509", "modified_by": "Administrator", "module": "Stock", - "name": "Pick Ticket", + "name": "Pick List", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/stock/doctype/pick_ticket/pick_ticket.py b/erpnext/stock/doctype/pick_list/pick_list.py similarity index 97% rename from erpnext/stock/doctype/pick_ticket/pick_ticket.py rename to erpnext/stock/doctype/pick_list/pick_list.py index 16f6d7a5df..3e45bddfd3 100644 --- a/erpnext/stock/doctype/pick_ticket/pick_ticket.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -7,7 +7,7 @@ import frappe from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc -class PickTicket(Document): +class PickList(Document): def set_item_locations(self): reference_items = self.reference_items @@ -131,14 +131,14 @@ def set_batch_no(item_doc, parent_doc): @frappe.whitelist() def make_delivery_note(source_name, target_doc=None): - target_doc = get_mapped_doc("Pick Ticket", source_name, { - "Pick Ticket": { + target_doc = get_mapped_doc("Pick List", source_name, { + "Pick List": { "doctype": "Delivery Note", # "validation": { # "docstatus": ["=", 1] # } }, - "Pick Ticket Item": { + "Pick List Item": { "doctype": "Delivery Note Item", "field_map": { "item": "item_code", diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py new file mode 100644 index 0000000000..ed4b6b58cc --- /dev/null +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +# test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch'] + +from erpnext.selling.doctype.sales_order.sales_order import make_pick_list + +class TestPickList(unittest.TestCase): + def test_pick_list_picks_warehouse_for_each_item(self): + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'reference_items': [{ + 'item': '_Test Item Home Desktop 100', + 'reference_doctype': 'Sales Order', + 'qty': 5, + 'reference_name': '_T-Sales Order-1', + }], + }) + + pick_list.set_item_locations() + + self.assertEqual(pick_list.items_locations[0].item, '_Test Item Home Desktop 100') + self.assertEqual(pick_list.items_locations[0].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_list.items_locations[0].qty, 5) + + def test_pick_list_skips_out_of_stock_item(self): + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'reference_items': [{ + 'item': '_Test Item Warehouse Group Wise Reorder', + 'reference_doctype': 'Sales Order', + 'qty': 1000, + 'reference_name': '_T-Sales Order-1', + }], + }) + + pick_list.set_item_locations() + + self.assertEqual(pick_list.items_locations[0].item, '_Test Item Warehouse Group Wise Reorder') + self.assertEqual(pick_list.items_locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') + self.assertEqual(pick_list.items_locations[0].qty, 30) + + + def test_pick_list_skips_items_in_expired_batch(self): + pass + + def test_pick_list_shows_serial_no_for_serialized_item(self): + + stock_reconciliation = frappe.get_doc({ + 'doctype': 'Stock Reconciliation', + 'company': '_Test Company', + 'items': [{ + 'item_code': '_Test Serialized Item', + 'warehouse': '_Test Warehouse - _TC', + 'qty': 5, + 'serial_no': '123450\n123451\n123452\n123453\n123454' + }] + }) + + stock_reconciliation.submit() + + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'reference_items': [{ + 'item': '_Test Serialized Item', + 'reference_doctype': 'Sales Order', + 'qty': 1000, + 'reference_name': '_T-Sales Order-1', + }], + }) + + pick_list.set_item_locations() + self.assertEqual(pick_list.items_locations[0].item, '_Test Serialized Item') + self.assertEqual(pick_list.items_locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') + self.assertEqual(pick_list.items_locations[0].qty, 30) + self.assertEqual(pick_list.items_locations[0].serial_no, 30) + + + def test_pick_list_for_multiple_reference_doctypes(self): + pass + + +## records required + +''' +batch no +items +sales invoice +stock entries + bin + stock ledger entry +warehouses +''' \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_ticket_item/__init__.py b/erpnext/stock/doctype/pick_list_item/pick_list_item/__init__.py similarity index 100% rename from erpnext/stock/doctype/pick_ticket_item/__init__.py rename to erpnext/stock/doctype/pick_list_item/pick_list_item/__init__.py diff --git a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item/pick_ticket_item.json similarity index 98% rename from erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json rename to erpnext/stock/doctype/pick_list_item/pick_list_item/pick_ticket_item.json index 67a7e9caa5..c83b696144 100644 --- a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item/pick_ticket_item.json @@ -132,7 +132,7 @@ "modified": "2019-07-30 23:47:53.566473", "modified_by": "Administrator", "module": "Stock", - "name": "Pick Ticket Item", + "name": "Pick List Item", "owner": "Administrator", "permissions": [], "quick_entry": 1, diff --git a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.py b/erpnext/stock/doctype/pick_list_item/pick_list_item/pick_ticket_item.py similarity index 88% rename from erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.py rename to erpnext/stock/doctype/pick_list_item/pick_list_item/pick_ticket_item.py index a13666a438..8797b8dc21 100644 --- a/erpnext/stock/doctype/pick_ticket_item/pick_ticket_item.py +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item/pick_ticket_item.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals # import frappe from frappe.model.document import Document -class PickTicketItem(Document): +class PickListItem(Document): pass diff --git a/erpnext/stock/doctype/pick_list_reference_item/__init__.py b/erpnext/stock/doctype/pick_list_reference_item/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.js b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.js similarity index 76% rename from erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.js rename to erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.js index a3f909659b..875ce23a28 100644 --- a/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.js +++ b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.js @@ -1,7 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Pick Ticket Reference Item', { +frappe.ui.form.on('Pick List 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_list_reference_item/pick_list_reference_item.json similarity index 96% rename from erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.json rename to erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json index c31e2bcd23..23ef2553a2 100644 --- a/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.json +++ b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json @@ -46,7 +46,7 @@ "modified": "2019-07-30 23:43:30.901151", "modified_by": "Administrator", "module": "Stock", - "name": "Pick Ticket Reference Item", + "name": "Pick List Reference Item", "owner": "Administrator", "permissions": [], "sort_field": "modified", diff --git a/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.py b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.py similarity index 85% rename from erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.py rename to erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.py index 412be75f88..74f0563cdf 100644 --- a/erpnext/stock/doctype/pick_ticket_reference_item/pick_ticket_reference_item.py +++ b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals # import frappe from frappe.model.document import Document -class PickTicketReferenceItem(Document): +class PickListReferenceItem(Document): pass diff --git a/erpnext/stock/doctype/pick_ticket/test_pick_ticket.py b/erpnext/stock/doctype/pick_ticket/test_pick_ticket.py deleted file mode 100644 index 5a41bc9f2c..0000000000 --- a/erpnext/stock/doctype/pick_ticket/test_pick_ticket.py +++ /dev/null @@ -1,100 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest -# test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch'] - -from erpnext.selling.doctype.sales_order.sales_order import make_pick_ticket - -class TestPickTicket(unittest.TestCase): - def test_pick_ticket_picks_warehouse_for_each_item(self): - pick_ticket = frappe.get_doc({ - 'doctype': 'Pick Ticket', - 'company': '_Test Company', - 'reference_items': [{ - 'item': '_Test Item Home Desktop 100', - 'reference_doctype': 'Sales Order', - 'qty': 5, - 'reference_name': '_T-Sales Order-1', - }], - }) - - pick_ticket.set_item_locations() - - self.assertEqual(pick_ticket.items_locations[0].item, '_Test Item Home Desktop 100') - self.assertEqual(pick_ticket.items_locations[0].warehouse, '_Test Warehouse - _TC') - self.assertEqual(pick_ticket.items_locations[0].qty, 5) - - def test_pick_ticket_skips_out_of_stock_item(self): - pick_ticket = frappe.get_doc({ - 'doctype': 'Pick Ticket', - 'company': '_Test Company', - 'reference_items': [{ - 'item': '_Test Item Warehouse Group Wise Reorder', - 'reference_doctype': 'Sales Order', - 'qty': 1000, - 'reference_name': '_T-Sales Order-1', - }], - }) - - pick_ticket.set_item_locations() - - self.assertEqual(pick_ticket.items_locations[0].item, '_Test Item Warehouse Group Wise Reorder') - self.assertEqual(pick_ticket.items_locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') - self.assertEqual(pick_ticket.items_locations[0].qty, 30) - - - def test_pick_ticket_skips_items_in_expired_batch(self): - pass - - def test_pick_ticket_shows_serial_no_for_serialized_item(self): - - stock_reconciliation = frappe.get_doc({ - 'doctype': 'Stock Reconciliation', - 'company': '_Test Company', - 'items': [{ - 'item_code': '_Test Serialized Item', - 'warehouse': '_Test Warehouse - _TC', - 'qty': 5, - 'serial_no': '123450\n123451\n123452\n123453\n123454' - }] - }) - - stock_reconciliation.submit() - - pick_ticket = frappe.get_doc({ - 'doctype': 'Pick Ticket', - 'company': '_Test Company', - 'reference_items': [{ - 'item': '_Test Serialized Item', - 'reference_doctype': 'Sales Order', - 'qty': 1000, - 'reference_name': '_T-Sales Order-1', - }], - }) - - pick_ticket.set_item_locations() - self.assertEqual(pick_ticket.items_locations[0].item, '_Test Serialized Item') - self.assertEqual(pick_ticket.items_locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') - self.assertEqual(pick_ticket.items_locations[0].qty, 30) - self.assertEqual(pick_ticket.items_locations[0].serial_no, 30) - - - def test_pick_ticket_for_multiple_reference_doctypes(self): - pass - - -## records required - -''' -batch no -items -sales invoice -stock entries - bin - stock ledger entry -warehouses -''' \ No newline at end of file From bb7a2dbb6a9f6052a695ed26fc7a597df8fef107 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 1 Aug 2019 12:38:26 +0530 Subject: [PATCH 16/79] fix: Rename file --- .../pick_list_item/{pick_ticket_item.json => pick_list_item.json} | 0 .../pick_list_item/{pick_ticket_item.py => pick_list_item.py} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename erpnext/stock/doctype/pick_list_item/pick_list_item/{pick_ticket_item.json => pick_list_item.json} (100%) rename erpnext/stock/doctype/pick_list_item/pick_list_item/{pick_ticket_item.py => pick_list_item.py} (100%) diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item/pick_ticket_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item/pick_list_item.json similarity index 100% rename from erpnext/stock/doctype/pick_list_item/pick_list_item/pick_ticket_item.json rename to erpnext/stock/doctype/pick_list_item/pick_list_item/pick_list_item.json diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item/pick_ticket_item.py b/erpnext/stock/doctype/pick_list_item/pick_list_item/pick_list_item.py similarity index 100% rename from erpnext/stock/doctype/pick_list_item/pick_list_item/pick_ticket_item.py rename to erpnext/stock/doctype/pick_list_item/pick_list_item/pick_list_item.py From e06f486aeed08c3236226b1c2f984bf8ada7f6a3 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 1 Aug 2019 12:54:33 +0530 Subject: [PATCH 17/79] fix: Rename methods --- erpnext/selling/doctype/sales_order/sales_order.js | 6 +++--- erpnext/selling/doctype/sales_order/sales_order.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 136ff55344..618adc4691 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -110,7 +110,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( let allow_delivery = false; if (doc.docstatus==1) { - this.frm.add_custom_button(__('Pick List'), () => this.make_pick_ticket(), __('Create')); + this.frm.add_custom_button(__('Pick List'), () => this.make_pick_list(), __('Create')); if(this.frm.has_perm("submit")) { if(doc.status === 'On Hold') { @@ -235,9 +235,9 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( this.order_type(doc); }, - make_pick_ticket() { + make_pick_list() { frappe.model.open_mapped_doc({ - method: "erpnext.selling.doctype.sales_order.sales_order.make_pick_ticket", + method: "erpnext.selling.doctype.sales_order.sales_order.make_pick_list", frm: this.frm }) }, diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 1d636e3c26..b79a2925e0 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -998,7 +998,7 @@ def make_inter_company_purchase_order(source_name, target_doc=None): return make_inter_company_transaction("Sales Order", source_name, target_doc) @frappe.whitelist() -def make_pick_ticket(source_name, target_doc=None): +def make_pick_list(source_name, target_doc=None): doc = get_mapped_doc("Sales Order", source_name, { "Sales Order": { "doctype": "Pick List", From a7dc3735aec3492d038f96c3867df0dc2df545ad Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 1 Aug 2019 15:49:29 +0530 Subject: [PATCH 18/79] fix: Move folders to right location --- .../stock/doctype/pick_list_item/{pick_list_item => }/__init__.py | 0 .../pick_list_item/{pick_list_item => }/pick_list_item.json | 0 .../doctype/pick_list_item/{pick_list_item => }/pick_list_item.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename erpnext/stock/doctype/pick_list_item/{pick_list_item => }/__init__.py (100%) rename erpnext/stock/doctype/pick_list_item/{pick_list_item => }/pick_list_item.json (100%) rename erpnext/stock/doctype/pick_list_item/{pick_list_item => }/pick_list_item.py (100%) diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item/__init__.py b/erpnext/stock/doctype/pick_list_item/__init__.py similarity index 100% rename from erpnext/stock/doctype/pick_list_item/pick_list_item/__init__.py rename to erpnext/stock/doctype/pick_list_item/__init__.py diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item/pick_list_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item.json similarity index 100% rename from erpnext/stock/doctype/pick_list_item/pick_list_item/pick_list_item.json rename to erpnext/stock/doctype/pick_list_item/pick_list_item.json diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item/pick_list_item.py b/erpnext/stock/doctype/pick_list_item/pick_list_item.py similarity index 100% rename from erpnext/stock/doctype/pick_list_item/pick_list_item/pick_list_item.py rename to erpnext/stock/doctype/pick_list_item/pick_list_item.py From b35c0410b178cc0bf4532080630794ec4eec8913 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 2 Aug 2019 08:12:30 +0530 Subject: [PATCH 19/79] fix: Add option to get items from work order --- .../doctype/work_order/work_order.js | 8 +++++++ .../doctype/work_order/work_order.py | 24 +++++++++++++++++++ erpnext/stock/doctype/pick_list/pick_list.js | 19 +++++++++++++-- erpnext/stock/doctype/pick_list/pick_list.py | 3 +-- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 22613cc8a4..a42fc65a8a 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -161,6 +161,10 @@ frappe.ui.form.on("Work Order", { frm.add_custom_button(__('Create BOM'), () => { frm.trigger("make_bom"); }); + + frm.add_custom_button(__('Pick List'), () => { + frm.trigger("make_bom"); + }, __('Make')); } }, @@ -264,6 +268,10 @@ frappe.ui.form.on("Work Order", { }); }, + make_pick_list() { + + }, + show_progress: function(frm) { var bars = []; var message = ''; diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 2b70325d9f..c489fbcd55 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -19,6 +19,7 @@ from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty from frappe.utils.csvutils import getlink from erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty from erpnext.utilities.transaction_base import validate_uom_is_integer +from frappe.model.mapper import get_mapped_doc class OverProductionError(frappe.ValidationError): pass class StockOverProductionError(frappe.ValidationError): pass @@ -707,3 +708,26 @@ def get_work_order_operation_data(work_order, operation, workstation): for d in work_order.operations: if d.operation == operation and d.workstation == workstation: return d + +@frappe.whitelist() +def make_pick_list(source_name, target_doc=None): + doc = get_mapped_doc("Work Order", source_name, { + "Work Order": { + "doctype": "Pick List", + "validation": { + "docstatus": ["=", 1] + } + }, + "Work Order Item": { + "doctype": "Pick List Reference Item", + "field_map": { + "item_code": "item", + "required_qty": "qty", + "parenttype": "reference_doctype", + "parent": "reference_name", + "name": "reference_document_item" + }, + }, + }, target_doc) + + return doc diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 4cf4cdf78a..602b7e02dd 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -14,13 +14,13 @@ frappe.ui.form.on('Pick List', { }, refresh: (frm) => { frm.add_custom_button(__('Delivery Note'), () => frm.trigger('make_delivery_note'), __('Create')); - frm.add_custom_button(__('Sales Order'), function() { + frm.add_custom_button(__('Sales Order'), () => { erpnext.utils.map_current_doc({ method: "erpnext.selling.doctype.sales_order.sales_order.make_pick_list", source_doctype: "Sales Order", target: frm, setters: { - company: frm.doc.company || undefined, + company: frm.doc.company, }, get_query_filters: { docstatus: 1, @@ -28,6 +28,21 @@ frappe.ui.form.on('Pick List', { }); }, __("Get items from")); + frm.add_custom_button(__('Work Order'), () => { + erpnext.utils.map_current_doc({ + method: "erpnext.manufacturing.doctype.work_order.work_order.make_pick_list", + source_doctype: "Work Order", + target: frm, + setters: { + company: frm.doc.company, + }, + date_field: 'creation', + get_query_filters: { + docstatus: 1, + } + }); + }, __("Get items from")); + if (frm.doc.reference_items && frm.doc.reference_items.length) { frm.add_custom_button(__('Get Item Locations'), () => { frm.call('set_item_locations'); diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 3e45bddfd3..77dacd5144 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -97,7 +97,6 @@ def set_batch_no(item_doc, parent_doc): 'item_code': item_doc.item, 'warehouse': item_doc.warehouse, }, as_dict=1) - print(batches) required_qty = item_doc.qty while required_qty > 0 and batches: @@ -114,8 +113,8 @@ def set_batch_no(item_doc, parent_doc): # split item if quantity of item in batch is less that required # Look for another batch - # set quantity of of item equal to batch quantity required_qty -= batch.qty + # set quantity of current item equal to batch quantity item_doc.set('qty', batch.qty) item_doc = parent_doc.append('items', { 'item': item_doc.item, From c57328669a4bba46ec9e1d003f4f052683116e30 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 2 Aug 2019 08:14:27 +0530 Subject: [PATCH 20/79] feat: Add pick list print format --- erpnext/stock/print_format/__init__.py | 0 .../stock/print_format/pick_list/__init__.py | 0 .../print_format/pick_list/pick_list.json | 23 +++++++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 erpnext/stock/print_format/__init__.py create mode 100644 erpnext/stock/print_format/pick_list/__init__.py create mode 100644 erpnext/stock/print_format/pick_list/pick_list.json diff --git a/erpnext/stock/print_format/__init__.py b/erpnext/stock/print_format/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/print_format/pick_list/__init__.py b/erpnext/stock/print_format/pick_list/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/print_format/pick_list/pick_list.json b/erpnext/stock/print_format/pick_list/pick_list.json new file mode 100644 index 0000000000..56c819fc0c --- /dev/null +++ b/erpnext/stock/print_format/pick_list/pick_list.json @@ -0,0 +1,23 @@ +{ + "align_labels_right": 1, + "creation": "2019-08-02 07:27:42.533305", + "custom_format": 0, + "disabled": 0, + "doc_type": "Pick List", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"
\\t\\t\\t\\t

Pick List
{{ doc.name }}\\t\\t\\t\\t

\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"parent_warehouse\", \"label\": \"Parent Warehouse\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"item_name\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"warehouse\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"serial_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"batch_no\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"item_locations\", \"label\": \"Item Locations\"}]", + "idx": 0, + "line_breaks": 1, + "modified": "2019-08-02 07:58:35.504361", + "modified_by": "Administrator", + "module": "Stock", + "name": "Pick List", + "owner": "Administrator", + "print_format_builder": 1, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 1, + "standard": "Yes" +} \ No newline at end of file From 6fd3a86012368e3f6039e1adbef071d9d88bac22 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 2 Aug 2019 16:48:31 +0530 Subject: [PATCH 21/79] fix: Option to create pick list from work order --- erpnext/manufacturing/doctype/work_order/work_order.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index a42fc65a8a..d345c0bf3f 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -163,7 +163,10 @@ frappe.ui.form.on("Work Order", { }); frm.add_custom_button(__('Pick List'), () => { - frm.trigger("make_bom"); + frappe.model.open_mapped_doc({ + method: "erpnext.manufacturing.doctype.work_order.work_order.make_pick_list", + frm + }); }, __('Make')); } }, From 182f4def0098664bf0aca82cad178da09606a890 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 16 Aug 2019 08:16:22 +0530 Subject: [PATCH 22/79] fix: Delivery note creation from pick list - Changes in serial no and batch no seletion - Changes in warehouse overrwite logic Co-authored-by: Nabin Hait --- erpnext/controllers/accounts_controller.py | 2 +- .../doctype/sales_order/sales_order.py | 45 +-- .../stock/doctype/pick_list/pick_list.json | 10 +- erpnext/stock/doctype/pick_list/pick_list.py | 266 ++++++++++++------ .../pick_list_item/pick_list_item.json | 152 +++++----- .../pick_list_reference_item.json | 88 ++++-- erpnext/stock/get_item_details.py | 37 ++- 7 files changed, 401 insertions(+), 199 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 14bf9d57a1..f6d4eee518 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -263,7 +263,7 @@ class AccountsController(TransactionBase): if self.get("is_subcontracted"): args["is_subcontracted"] = self.is_subcontracted - ret = get_item_details(args, self) + ret = get_item_details(args, self, overwrite_warehouse=False) for fieldname, value in ret.items(): if item.meta.get_field(fieldname) and value is not None: diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index b79a2925e0..2dc3788568 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -568,7 +568,7 @@ def make_project(source_name, target_doc=None): return doc @frappe.whitelist() -def make_delivery_note(source_name, target_doc=None): +def make_delivery_note(source_name, target_doc=None, skip_item_mapping=False): def set_missing_values(source, target): target.ignore_pricing_rule = 1 target.run_method("set_missing_values") @@ -593,23 +593,13 @@ def make_delivery_note(source_name, target_doc=None): or item.get("buying_cost_center") \ or item_group.get("buying_cost_center") - target_doc = get_mapped_doc("Sales Order", source_name, { + mapper = { "Sales Order": { "doctype": "Delivery Note", "validation": { "docstatus": ["=", 1] } }, - "Sales Order Item": { - "doctype": "Delivery Note Item", - "field_map": { - "rate": "rate", - "name": "so_detail", - "parent": "against_sales_order", - }, - "postprocess": update_item, - "condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 - }, "Sales Taxes and Charges": { "doctype": "Sales Taxes and Charges", "add_if_empty": True @@ -618,7 +608,21 @@ def make_delivery_note(source_name, target_doc=None): "doctype": "Sales Team", "add_if_empty": True } - }, target_doc, set_missing_values) + } + + if not skip_item_mapping: + mapper["Sales Order Item"] = { + "doctype": "Delivery Note Item", + "field_map": { + "rate": "rate", + "name": "so_detail", + "parent": "against_sales_order", + }, + "postprocess": update_item, + "condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 + } + + target_doc = get_mapped_doc("Sales Order", source_name, mapper, target_doc, set_missing_values) return target_doc @@ -999,9 +1003,16 @@ def make_inter_company_purchase_order(source_name, target_doc=None): @frappe.whitelist() def make_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) + doc = get_mapped_doc("Sales Order", source_name, { "Sales Order": { "doctype": "Pick List", + "field_map": { + "doctype": "items_based_on" + }, "validation": { "docstatus": ["=", 1] } @@ -1009,11 +1020,11 @@ def make_pick_list(source_name, target_doc=None): "Sales Order Item": { "doctype": "Pick List Reference Item", "field_map": { - "item_code": "item", - "parenttype": "reference_doctype", - "parent": "reference_name", - "name": "reference_document_item" + "parent": "sales_order", + "name": "sales_order_item" }, + "postprocess": update_item_quantity, + "conditions": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 }, }, target_doc) diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 3f96630faf..c7a5fc8c75 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -7,6 +7,7 @@ "field_order": [ "company", "column_break_4", + "items_based_on", "parent_warehouse", "section_break_4", "reference_items", @@ -50,9 +51,16 @@ "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": "\nSales Order\nWork Order" } ], - "modified": "2019-08-01 10:50:17.055509", + "modified": "2019-08-13 19:30:01.151720", "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 77dacd5144..20b4230002 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -5,7 +5,10 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -from frappe.model.mapper import get_mapped_doc +from six import iteritems +from frappe.model.mapper import get_mapped_doc, map_child_doc +from frappe.utils import floor, flt, today +from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note as make_delivery_note_from_sales_order class PickList(Document): def set_item_locations(self): @@ -17,46 +20,56 @@ class PickList(Document): # Reset self.delete_key('item_locations') - for item in reference_items: - data = get_items_with_warehouse_and_quantity(item, from_warehouses) - for item_info in data: - print(self.append('item_locations', item_info)) + 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) + else: + item_locations = get_items_with_warehouse_and_quantity(item_doc, from_warehouses) - for item_doc in self.get('item_locations'): - if frappe.get_cached_value('Item', item_doc.item, 'has_serial_no'): - set_serial_nos(item_doc) - elif frappe.get_cached_value('Item', item_doc.item, 'has_batch_no'): - set_batch_no(item_doc, self) + for row in item_locations: + row.update({ + 'item_code': item_doc.item_code, + 'sales_order': item_doc.sales_order, + 'sales_order_item': item_doc.sales_order_item, + 'uom': item_doc.uom, + 'stock_uom': item_doc.stock_uom, + 'conversion_factor': item_doc.conversion_factor, + '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) def get_items_with_warehouse_and_quantity(item_doc, from_warehouses): - items = [] - item_locations = get_available_items(item_doc.item, from_warehouses) - remaining_qty = item_doc.qty + item_locations = [] + item_location_map = get_available_items(item_doc.item_code, from_warehouses) + remaining_stock_qty = item_doc.stock_qty + while remaining_stock_qty > 0 and item_location_map: + item_location = item_location_map.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) - 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, + uom_must_be_whole_number = frappe.db.get_value("UOM", item_doc.uom, "must_be_whole_number") + if uom_must_be_whole_number: + qty = floor(qty) + stock_qty = qty * item_doc.conversion_factor + + item_locations.append({ 'qty': qty, - 'warehouse': item_location.warehouse, - 'reference_doctype': item_doc.reference_doctype, - 'reference_name': item_doc.reference_name, - 'reference_document_item': item_doc.reference_document_item, + 'warehouse': item_location.warehouse }) - remaining_qty -= qty + remaining_stock_qty -= stock_qty - if remaining_qty: - frappe.msgprint('{} qty of {} is out of stock. Skipping...'.format(remaining_qty, item_doc.item)) - return items + 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 - return items - -def get_available_items(item, from_warehouses): +def get_available_items(item_code, from_warehouses): # gets all items available in different warehouses - # FIFO filters = frappe._dict({ - 'item_code': item, + 'item_code': item_code, 'actual_qty': ['>', 0] }) if from_warehouses: @@ -71,79 +84,170 @@ def get_available_items(item, from_warehouses): def set_serial_nos(item_doc): serial_nos = frappe.get_all('Serial No', { - 'item_code': item_doc.item, + 'item_code': item_doc.item_code, 'warehouse': item_doc.warehouse - }, limit=item_doc.qty, order_by='purchase_date') + }, limit=item_doc.stock_qty, order_by='purchase_date') item_doc.set('serial_no', '\n'.join([serial_no.name for serial_no in serial_nos])) - # should we assume that all serialized item available in stock will have serial no? + # should we assume that all serialized item_code available in stock will have serial no? -def set_batch_no(item_doc, parent_doc): - batches = frappe.db.sql(""" +def get_item_locations_based_on_serial_nos(item_doc): + serial_nos = frappe.get_all('Serial No', + fields = ['name', 'warehouse'], + filters = { + 'item_code': item_doc.item_code, + 'warehouse': ['!=', ''] + }, limit=item_doc.stock_qty, order_by='purchase_date', as_list=1) + + remaining_stock_qty = flt(item_doc.stock_qty) - len(serial_nos) + if remaining_stock_qty: + frappe.msgprint('{0} {1} of {2} is not available.' + .format(remaining_stock_qty, item_doc.stock_uom, item_doc.item_code)) + + warehouse_serial_nos_map = frappe._dict() + for serial_no, warehouse in serial_nos: + warehouse_serial_nos_map.setdefault(warehouse, []).append(serial_no) + + item_locations = [] + for warehouse, serial_nos in iteritems(warehouse_serial_nos_map): + item_locations.append({ + 'qty': len(serial_nos), + 'warehouse': warehouse, + 'serial_no': '\n'.join(serial_nos) + }) + + return item_locations + +def get_item_locations_based_on_batch_nos(item_doc): + batch_qty = frappe.db.sql(""" SELECT - `batch_no`, - SUM(`actual_qty`) AS `qty` + sle.`warehouse`, + sle.`batch_no`, + SUM(sle.`actual_qty`) AS `qty` FROM - `tabStock Ledger Entry` + `tabStock Ledger Entry` sle, `tabBatch` batch WHERE - `item_code`=%(item_code)s - AND `warehouse`=%(warehouse)s + sle.batch_no = batch.name + and sle.`item_code`=%(item_code)s + and IFNULL(batch.expiry_date, '2200-01-01') > %(today)s GROUP BY `warehouse`, `batch_no`, `item_code` HAVING `qty` > 0 + ORDER BY IFNULL(batch.expiry_date, '2200-01-01') """, { - 'item_code': item_doc.item, - 'warehouse': item_doc.warehouse, + 'item_code': item_doc.item_code, + 'today': today() }, as_dict=1) + item_locations = [] required_qty = item_doc.qty - while required_qty > 0 and batches: - batch = batches.pop() - batch_expiry = frappe.get_value('Batch', batch.batch_no, 'expiry_date') - if batch_expiry and batch_expiry <= frappe.utils.getdate(): - frappe.msgprint('Skipping expired Batch {}'.format(batch.batch_no)) - continue - item_doc.batch_no = batch.batch_no - if batch.qty >= item_doc.qty: - required_qty = 0 - break + for d in batch_qty: + if d.qty > required_qty: + d.qty = required_qty else: - # split item if quantity of item in batch is less that required - # Look for another batch + required_qty -= d.qty + + item_locations.append(d) + + if required_qty <= 0: + break + + # required_qty = item_doc.qty + # while required_qty > 0 and batches: + # batch = batches.pop() + # batch_expiry = frappe.get_value('Batch', batch.batch_no, 'expiry_date') + # if batch_expiry and batch_expiry <= frappe.utils.getdate(): + # frappe.msgprint('Skipping expired Batch {}'.format(batch.batch_no)) + # continue + # item_doc.batch_no = batch.batch_no + # if batch.qty >= item_doc.qty: + # required_qty = 0 + # break + # else: + # # split item_code if quantity of item_code in batch is less that required + # # Look for another batch + + # required_qty -= batch.qty + # # set quantity of current item_code equal to batch quantity + # item_doc.set('qty', batch.qty) + # item_doc = parent_doc.append('items', { + # 'item_code': item_doc.item_code, + # 'qty': required_qty, + # 'warehouse': item_doc.warehouse, + # 'sales_order': item_doc.sales_order, + # 'sales_order_item': item_doc.sales_order_item, + # 'uom': item_doc.uom, + # 'stock_uom': item_doc.stock_uom, + # 'conversion_factor': item_doc.conversion_factor, + # 'stock_qty': qty * item_doc.conversion_factor, + # }) - required_qty -= batch.qty - # set quantity of current item equal to batch quantity - item_doc.set('qty', batch.qty) - item_doc = parent_doc.append('items', { - 'item': item_doc.item, - 'qty': required_qty, - 'warehouse': item_doc.warehouse, - 'reference_doctype': item_doc.reference_doctype, - 'reference_name': item_doc.reference_name, - 'reference_document_item': item_doc.reference_document_item, - }) if required_qty: - frappe.msgprint('No batches found for {} qty of {}. Skipping...'.format(required_qty, item_doc.item)) - parent_doc.remove(item_doc) + frappe.msgprint('No batches found for {} qty of {}.'.format(required_qty, item_doc.item_code)) + + return item_locations @frappe.whitelist() def make_delivery_note(source_name, target_doc=None): - target_doc = get_mapped_doc("Pick List", source_name, { - "Pick List": { - "doctype": "Delivery Note", - # "validation": { - # "docstatus": ["=", 1] - # } - }, - "Pick List Item": { + pick_list = frappe.get_doc('Pick List', source_name) + sales_orders = [d.sales_order for d in pick_list.item_locations] + sales_orders = set(sales_orders) + + delivery_note = None + for sales_order in sales_orders: + delivery_note = make_delivery_note_from_sales_order(sales_order, + delivery_note, skip_item_mapping=True) + + for location in pick_list.item_locations: + sales_order_item = frappe.get_cached_doc('Sales Order Item', location.sales_order_item) + item_table_mapper = { "doctype": "Delivery Note Item", "field_map": { - "item": "item_code", - "reference_docname": "against_sales_order", + "rate": "rate", + "name": "so_detail", + "parent": "against_sales_order", }, - }, - }, target_doc) + "condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 + } - return target_doc \ No newline at end of file + dn_item = map_child_doc(sales_order_item, delivery_note, item_table_mapper) + + if dn_item: + dn_item.warehouse = location.warehouse + dn_item.qty = location.qty + + update_delivery_note_item(sales_order_item, dn_item, delivery_note) + + set_delivery_note_missing_values(delivery_note) + + return delivery_note + + +def set_delivery_note_missing_values(target): + target.run_method("set_missing_values") + target.run_method("set_po_nos") + target.run_method("calculate_taxes_and_totals") + +def update_delivery_note_item(source, target, delivery_note): + cost_center = frappe.db.get_value("Project", delivery_note.project, "cost_center") + if not cost_center: + cost_center = frappe.db.get_value('Item Default', + fieldname=['buying_cost_center'], + filters={ + 'parent': source.item_code, + 'parenttype': 'Item', + 'company': delivery_note.company + }) + + if not cost_center: + cost_center = frappe.db.get_value('Item Default', + fieldname=['buying_cost_center'], + filters={ + 'parent': source.item_group, + 'parenttype': 'Item Group', + 'company': delivery_note.company + }) + + target.cost_center = cost_center \ No newline at end of file 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 c83b696144..9ee806acf5 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -4,32 +4,29 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "item", + "item_code", "item_name", "column_break_2", "description", - "has_batch_no", - "has_serial_no", "section_break_5", "warehouse", + "quantity_section", "qty", + "stock_qty", "picked_qty", + "column_break_11", + "uom", + "stock_uom", + "conversion_factor", + "serial_no_and_batch_section", "serial_no", + "column_break_20", "batch_no", - "reference_section", - "reference_doctype", - "reference_name", - "reference_document_item" + "column_break_15", + "sales_order", + "sales_order_item" ], "fields": [ - { - "fieldname": "item", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Item", - "options": "Item", - "read_only": 1 - }, { "fieldname": "qty", "fieldtype": "Float", @@ -52,72 +49,31 @@ "read_only": 1 }, { - "fetch_from": "item.item_name", + "fetch_from": "item_code.item_name", "fieldname": "item_name", "fieldtype": "Data", "label": "Item Name", "read_only": 1 }, { - "fetch_from": "item.description", + "fetch_from": "item_code.description", "fieldname": "description", "fieldtype": "Text", "label": "Description", "read_only": 1 }, { - "fieldname": "reference_document_item", - "fieldtype": "Data", - "label": "Reference Document Item", - "read_only": 1 - }, - { + "depends_on": "serial_no", "fieldname": "serial_no", "fieldtype": "Small Text", - "label": "Serial No", - "read_only": 1 + "label": "Serial No" }, { + "depends_on": "batch_no", "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 + "options": "Batch" }, { "fieldname": "column_break_2", @@ -126,10 +82,80 @@ { "fieldname": "section_break_5", "fieldtype": "Section Break" + }, + { + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "read_only": 1 + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "uom", + "fieldtype": "Link", + "label": "UOM", + "options": "UOM", + "read_only": 1 + }, + { + "fieldname": "conversion_factor", + "fieldtype": "Float", + "label": "UOM Conversion Factor", + "read_only": 1 + }, + { + "fieldname": "stock_qty", + "fieldtype": "Float", + "label": "Qty as per Stock UOM", + "read_only": 1 + }, + { + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item", + "options": "Item", + "read_only": 1 + }, + { + "fieldname": "quantity_section", + "fieldtype": "Section Break", + "label": "Quantity" + }, + { + "fieldname": "column_break_15", + "fieldtype": "Section Break", + "label": "Reference" + }, + { + "fieldname": "sales_order", + "fieldtype": "Link", + "label": "Sales Order", + "options": "Sales Order", + "read_only": 1 + }, + { + "fieldname": "sales_order_item", + "fieldtype": "Data", + "label": "Sales Order Item", + "read_only": 1 + }, + { + "fieldname": "serial_no_and_batch_section", + "fieldtype": "Section Break", + "label": "Serial No and Batch" + }, + { + "fieldname": "column_break_20", + "fieldtype": "Column Break" } ], "istable": 1, - "modified": "2019-07-30 23:47:53.566473", + "modified": "2019-08-14 18:41:37.727388", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", diff --git a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json index 23ef2553a2..0aa94feb37 100644 --- a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json +++ b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json @@ -4,32 +4,19 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "item", + "item_code", + "quantity_section", "qty", - "reference_doctype", - "reference_name", - "reference_document_item" + "stock_qty", + "column_break_5", + "uom", + "stock_uom", + "conversion_factor", + "reference_section", + "sales_order", + "sales_order_item" ], "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", @@ -37,13 +24,62 @@ "label": "Qty" }, { - "fieldname": "reference_document_item", + "fieldname": "quantity_section", + "fieldtype": "Section Break", + "label": "Quantity" + }, + { + "fieldname": "stock_qty", + "fieldtype": "Float", + "label": "Stock Qty" + }, + { + "fieldname": "uom", + "fieldtype": "Link", + "label": "UOM", + "options": "UOM" + }, + { + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM" + }, + { + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item", + "options": "Item" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "reference_section", + "fieldtype": "Section Break", + "label": "Reference" + }, + { + "fieldname": "sales_order", + "fieldtype": "Link", + "label": "Sales Order", + "options": "Sales Order" + }, + { + "fieldname": "sales_order_item", "fieldtype": "Data", - "label": "Reference Document Item" + "label": "Sales Order Item" + }, + { + "fieldname": "conversion_factor", + "fieldtype": "Float", + "label": "UOM Conversion Factor" } ], "istable": 1, - "modified": "2019-07-30 23:43:30.901151", + "modified": "2019-08-14 18:38:28.867113", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Reference Item", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index f1d784c8d9..41101f4678 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -22,7 +22,7 @@ sales_doctypes = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice'] purchase_doctypes = ['Material Request', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] @frappe.whitelist() -def get_item_details(args, doc=None): +def get_item_details(args, doc=None, overwrite_warehouse=True): """ args = { "item_code": "", @@ -44,11 +44,15 @@ def get_item_details(args, doc=None): "set_warehouse": "" } """ + args = process_args(args) + print('warehouse', args.warehouse, '========') item = frappe.get_cached_doc("Item", args.item_code) validate_item_details(args, item) - out = get_basic_details(args, item) + out = get_basic_details(args, item, overwrite_warehouse) + + print('warehouse2', out.warehouse, '========') get_item_tax_template(args, item, out) out["item_tax_rate"] = get_item_tax_map(args.company, args.get("item_tax_template") if out.get("item_tax_template") is None \ @@ -178,7 +182,7 @@ def validate_item_details(args, item): throw(_("Item {0} must be a Sub-contracted Item").format(item.name)) -def get_basic_details(args, item): +def get_basic_details(args, item, overwrite_warehouse=True): """ :param args: { "item_code": "", @@ -225,14 +229,26 @@ def get_basic_details(args, item): item_group_defaults = get_item_group_defaults(item.name, args.company) brand_defaults = get_brand_defaults(item.name, args.company) - warehouse = (args.get("set_warehouse") or item_defaults.get("default_warehouse") or - item_group_defaults.get("default_warehouse") or brand_defaults.get("default_warehouse") or args.warehouse) + if overwrite_warehouse or not args.warehouse: + warehouse = ( + args.get("set_warehouse") or + item_defaults.get("default_warehouse") or + item_group_defaults.get("default_warehouse") or + brand_defaults.get("default_warehouse") or + args.warehouse + ) - if not warehouse: - defaults = frappe.defaults.get_defaults() or {} - if defaults.get("default_warehouse") and frappe.db.exists("Warehouse", - {'name': defaults.default_warehouse, 'company': args.company}): - warehouse = defaults.default_warehouse + if not warehouse: + defaults = frappe.defaults.get_defaults() or {} + warehouse_exists = frappe.db.exists("Warehouse", { + 'name': defaults.default_warehouse, + 'company': args.company + }) + if defaults.get("default_warehouse") and warehouse_exists: + warehouse = defaults.default_warehouse + + else: + warehouse = args.warehouse if args.get('doctype') == "Material Request" and not args.get('material_request_type'): args['material_request_type'] = frappe.db.get_value('Material Request', @@ -784,6 +800,7 @@ def get_projected_qty(item_code, warehouse): @frappe.whitelist() def get_bin_details(item_code, warehouse): + print(item_code, warehouse, '---------------------------') return frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, ["projected_qty", "actual_qty", "reserved_qty"], as_dict=True, cache=True) \ or {"projected_qty": 0, "actual_qty": 0, "reserved_qty": 0} From 669cff88d147417caf3d1f35b0c09d1a62afa55a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 19 Aug 2019 10:38:01 +0530 Subject: [PATCH 23/79] fix: Get items button --- erpnext/stock/doctype/pick_list/pick_list.js | 57 ++++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 602b7e02dd..cb3cec6ab6 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -14,24 +14,35 @@ frappe.ui.form.on('Pick List', { }, refresh: (frm) => { frm.add_custom_button(__('Delivery Note'), () => frm.trigger('make_delivery_note'), __('Create')); - frm.add_custom_button(__('Sales Order'), () => { - erpnext.utils.map_current_doc({ - method: "erpnext.selling.doctype.sales_order.sales_order.make_pick_list", - source_doctype: "Sales Order", - target: frm, - setters: { - company: frm.doc.company, - }, - get_query_filters: { - docstatus: 1, - } - }); - }, __("Get items from")); - frm.add_custom_button(__('Work Order'), () => { + if (frm.doc.reference_items && frm.doc.reference_items.length) { + frm.add_custom_button(__('Get Item Locations'), () => { + frm.call('set_item_locations'); + }); + } + + frm.trigger('add_get_items_button'); + }, + items_based_on: (frm) => { + frm.trigger('add_get_items_button'); + }, + make_delivery_note(frm) { + frappe.model.open_mapped_doc({ + method: "erpnext.stock.doctype.pick_list.pick_list.make_delivery_note", + frm: frm + }); + }, + add_get_items_button(frm) { + frm.remove_custom_button(__("Get items")); + let source_doctype = frm.doc.items_based_on; + let method = 'erpnext.selling.doctype.sales_order.sales_order.make_pick_list'; + if (source_doctype === 'Sales Order') { + method = 'erpnext.manufacturing.doctype.work_order.work_order.make_pick_list'; + } + frm.add_custom_button(__("Get items"), () => { erpnext.utils.map_current_doc({ - method: "erpnext.manufacturing.doctype.work_order.work_order.make_pick_list", - source_doctype: "Work Order", + method: method, + source_doctype: source_doctype, target: frm, setters: { company: frm.doc.company, @@ -41,18 +52,6 @@ frappe.ui.form.on('Pick List', { docstatus: 1, } }); - }, __("Get items from")); - - if (frm.doc.reference_items && frm.doc.reference_items.length) { - frm.add_custom_button(__('Get Item Locations'), () => { - frm.call('set_item_locations'); - }); - } - }, - make_delivery_note(frm) { - frappe.model.open_mapped_doc({ - method: "erpnext.stock.doctype.pick_list.pick_list.make_delivery_note", - frm: frm }); - }, + } }); From 6450be5e58fa070badf11bb03a2274a00f9e96d1 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 19 Aug 2019 10:48:21 +0530 Subject: [PATCH 24/79] fix: Get Items button --- erpnext/stock/doctype/pick_list/pick_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index cb3cec6ab6..2e72d98d3f 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -36,7 +36,7 @@ frappe.ui.form.on('Pick List', { frm.remove_custom_button(__("Get items")); let source_doctype = frm.doc.items_based_on; let method = 'erpnext.selling.doctype.sales_order.sales_order.make_pick_list'; - if (source_doctype === 'Sales Order') { + if (source_doctype === 'Work Order') { method = 'erpnext.manufacturing.doctype.work_order.work_order.make_pick_list'; } frm.add_custom_button(__("Get items"), () => { From a251267dd7ae14c1ca32db0429222e1176d5ac51 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 20 Aug 2019 12:03:03 +0530 Subject: [PATCH 25/79] fix: Quantity while creating pick list from work order - Fix get_mapped_doc config to filter items - Fix erroneous condition in get_mapped_doc used for creation of pick_list in sales order --- .../doctype/work_order/work_order.py | 17 +++--- .../doctype/sales_order/sales_order.py | 2 +- erpnext/stock/doctype/pick_list/pick_list.js | 26 +++++++-- .../stock/doctype/pick_list/pick_list.json | 4 +- erpnext/stock/doctype/pick_list/pick_list.py | 57 +++++++++---------- 5 files changed, 60 insertions(+), 46 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index c489fbcd55..c653deece3 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -711,6 +711,14 @@ def get_work_order_operation_data(work_order, operation, workstation): @frappe.whitelist() def make_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 + target.stock_qty = qty + target.uom = frappe.get_value('Item', source.item_code, 'stock_uom') + target.stock_uom = target.uom + target.conversion_factor = 1 + doc = get_mapped_doc("Work Order", source_name, { "Work Order": { "doctype": "Pick List", @@ -720,13 +728,8 @@ def make_pick_list(source_name, target_doc=None): }, "Work Order Item": { "doctype": "Pick List Reference Item", - "field_map": { - "item_code": "item", - "required_qty": "qty", - "parenttype": "reference_doctype", - "parent": "reference_name", - "name": "reference_document_item" - }, + "postprocess": update_item_quantity, + "condition": lambda doc: abs(doc.transferred_qty) < abs(doc.required_qty) }, }, target_doc) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 2dc3788568..dd156d0473 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1024,7 +1024,7 @@ def make_pick_list(source_name, target_doc=None): "name": "sales_order_item" }, "postprocess": update_item_quantity, - "conditions": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 + "condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 }, }, target_doc) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 2e72d98d3f..b9b317b732 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -28,17 +28,35 @@ frappe.ui.form.on('Pick List', { }, make_delivery_note(frm) { frappe.model.open_mapped_doc({ - method: "erpnext.stock.doctype.pick_list.pick_list.make_delivery_note", + method: 'erpnext.stock.doctype.pick_list.pick_list.make_delivery_note', frm: frm }); }, 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; + let get_query_filters = { + docstatus: 1, + per_delivered: ['<', 100], + status: ['!=', ''] + }; 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"), () => { erpnext.utils.map_current_doc({ method: method, @@ -47,10 +65,8 @@ frappe.ui.form.on('Pick List', { setters: { company: frm.doc.company, }, - date_field: 'creation', - get_query_filters: { - docstatus: 1, - } + date_field: date_field, + get_query: get_query }); }); } diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index c7a5fc8c75..70272bf584 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -57,10 +57,10 @@ "fieldname": "items_based_on", "fieldtype": "Select", "label": "Items Based On", - "options": "\nSales Order\nWork Order" + "options": "Sales Order\nWork Order" } ], - "modified": "2019-08-13 19:30:01.151720", + "modified": "2019-08-19 12:31:54.023456", "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 20b4230002..91bdcb33ee 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -10,6 +10,8 @@ from frappe.model.mapper import get_mapped_doc, map_child_doc from frappe.utils import floor, flt, today from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note as make_delivery_note_from_sales_order +# TODO: Prioritize SO or WO group warehouse + class PickList(Document): def set_item_locations(self): reference_items = self.reference_items @@ -154,36 +156,6 @@ def get_item_locations_based_on_batch_nos(item_doc): if required_qty <= 0: break - # required_qty = item_doc.qty - # while required_qty > 0 and batches: - # batch = batches.pop() - # batch_expiry = frappe.get_value('Batch', batch.batch_no, 'expiry_date') - # if batch_expiry and batch_expiry <= frappe.utils.getdate(): - # frappe.msgprint('Skipping expired Batch {}'.format(batch.batch_no)) - # continue - # item_doc.batch_no = batch.batch_no - # if batch.qty >= item_doc.qty: - # required_qty = 0 - # break - # else: - # # split item_code if quantity of item_code in batch is less that required - # # Look for another batch - - # required_qty -= batch.qty - # # set quantity of current item_code equal to batch quantity - # item_doc.set('qty', batch.qty) - # item_doc = parent_doc.append('items', { - # 'item_code': item_doc.item_code, - # 'qty': required_qty, - # 'warehouse': item_doc.warehouse, - # 'sales_order': item_doc.sales_order, - # 'sales_order_item': item_doc.sales_order_item, - # 'uom': item_doc.uom, - # 'stock_uom': item_doc.stock_uom, - # 'conversion_factor': item_doc.conversion_factor, - # 'stock_qty': qty * item_doc.conversion_factor, - # }) - if required_qty: frappe.msgprint('No batches found for {} qty of {}.'.format(required_qty, item_doc.item_code)) @@ -250,4 +222,27 @@ def update_delivery_note_item(source, target, delivery_note): 'company': delivery_note.company }) - target.cost_center = cost_center \ No newline at end of file + target.cost_center = cost_center + +@frappe.whitelist() +def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filters, as_dict): + return frappe.db.sql(""" + SELECT + `name`, `company`, `planned_start_date` + FROM + `tabWork Order` + WHERE + `qty` > `produced_qty` + AND `status` not in ('Completed', 'Stopped') + AND name like %(txt)s + AND docstatus = 1 + ORDER BY + if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), name + LIMIT + %(start)s, %(page_length)s""", + { + 'txt': "%%%s%%" % txt, + '_txt': txt.replace('%', ''), + 'start': start, + 'page_length': frappe.utils.cint(page_length), + }, as_dict=as_dict) From 1a090432d979b100757c943f7aa4d773cdfb31f6 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 20 Aug 2019 12:04:07 +0530 Subject: [PATCH 26/79] fix: Quantity while creating pick list from work order - Fix get_mapped_doc config to filter items - Fix erroneous condition in get_mapped_doc used for creation of pick_list in sales order Co-authored-by: Nabin Hait --- .../doctype/work_order/work_order.py | 17 +++--- .../doctype/sales_order/sales_order.py | 2 +- erpnext/stock/doctype/pick_list/pick_list.js | 26 +++++++-- .../stock/doctype/pick_list/pick_list.json | 4 +- erpnext/stock/doctype/pick_list/pick_list.py | 57 +++++++++---------- 5 files changed, 60 insertions(+), 46 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index c489fbcd55..c653deece3 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -711,6 +711,14 @@ def get_work_order_operation_data(work_order, operation, workstation): @frappe.whitelist() def make_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 + target.stock_qty = qty + target.uom = frappe.get_value('Item', source.item_code, 'stock_uom') + target.stock_uom = target.uom + target.conversion_factor = 1 + doc = get_mapped_doc("Work Order", source_name, { "Work Order": { "doctype": "Pick List", @@ -720,13 +728,8 @@ def make_pick_list(source_name, target_doc=None): }, "Work Order Item": { "doctype": "Pick List Reference Item", - "field_map": { - "item_code": "item", - "required_qty": "qty", - "parenttype": "reference_doctype", - "parent": "reference_name", - "name": "reference_document_item" - }, + "postprocess": update_item_quantity, + "condition": lambda doc: abs(doc.transferred_qty) < abs(doc.required_qty) }, }, target_doc) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 2dc3788568..dd156d0473 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1024,7 +1024,7 @@ def make_pick_list(source_name, target_doc=None): "name": "sales_order_item" }, "postprocess": update_item_quantity, - "conditions": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 + "condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 }, }, target_doc) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 2e72d98d3f..b9b317b732 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -28,17 +28,35 @@ frappe.ui.form.on('Pick List', { }, make_delivery_note(frm) { frappe.model.open_mapped_doc({ - method: "erpnext.stock.doctype.pick_list.pick_list.make_delivery_note", + method: 'erpnext.stock.doctype.pick_list.pick_list.make_delivery_note', frm: frm }); }, 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; + let get_query_filters = { + docstatus: 1, + per_delivered: ['<', 100], + status: ['!=', ''] + }; 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"), () => { erpnext.utils.map_current_doc({ method: method, @@ -47,10 +65,8 @@ frappe.ui.form.on('Pick List', { setters: { company: frm.doc.company, }, - date_field: 'creation', - get_query_filters: { - docstatus: 1, - } + date_field: date_field, + get_query: get_query }); }); } diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index c7a5fc8c75..70272bf584 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -57,10 +57,10 @@ "fieldname": "items_based_on", "fieldtype": "Select", "label": "Items Based On", - "options": "\nSales Order\nWork Order" + "options": "Sales Order\nWork Order" } ], - "modified": "2019-08-13 19:30:01.151720", + "modified": "2019-08-19 12:31:54.023456", "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 20b4230002..91bdcb33ee 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -10,6 +10,8 @@ from frappe.model.mapper import get_mapped_doc, map_child_doc from frappe.utils import floor, flt, today from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note as make_delivery_note_from_sales_order +# TODO: Prioritize SO or WO group warehouse + class PickList(Document): def set_item_locations(self): reference_items = self.reference_items @@ -154,36 +156,6 @@ def get_item_locations_based_on_batch_nos(item_doc): if required_qty <= 0: break - # required_qty = item_doc.qty - # while required_qty > 0 and batches: - # batch = batches.pop() - # batch_expiry = frappe.get_value('Batch', batch.batch_no, 'expiry_date') - # if batch_expiry and batch_expiry <= frappe.utils.getdate(): - # frappe.msgprint('Skipping expired Batch {}'.format(batch.batch_no)) - # continue - # item_doc.batch_no = batch.batch_no - # if batch.qty >= item_doc.qty: - # required_qty = 0 - # break - # else: - # # split item_code if quantity of item_code in batch is less that required - # # Look for another batch - - # required_qty -= batch.qty - # # set quantity of current item_code equal to batch quantity - # item_doc.set('qty', batch.qty) - # item_doc = parent_doc.append('items', { - # 'item_code': item_doc.item_code, - # 'qty': required_qty, - # 'warehouse': item_doc.warehouse, - # 'sales_order': item_doc.sales_order, - # 'sales_order_item': item_doc.sales_order_item, - # 'uom': item_doc.uom, - # 'stock_uom': item_doc.stock_uom, - # 'conversion_factor': item_doc.conversion_factor, - # 'stock_qty': qty * item_doc.conversion_factor, - # }) - if required_qty: frappe.msgprint('No batches found for {} qty of {}.'.format(required_qty, item_doc.item_code)) @@ -250,4 +222,27 @@ def update_delivery_note_item(source, target, delivery_note): 'company': delivery_note.company }) - target.cost_center = cost_center \ No newline at end of file + target.cost_center = cost_center + +@frappe.whitelist() +def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filters, as_dict): + return frappe.db.sql(""" + SELECT + `name`, `company`, `planned_start_date` + FROM + `tabWork Order` + WHERE + `qty` > `produced_qty` + AND `status` not in ('Completed', 'Stopped') + AND name like %(txt)s + AND docstatus = 1 + ORDER BY + if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), name + LIMIT + %(start)s, %(page_length)s""", + { + 'txt': "%%%s%%" % txt, + '_txt': txt.replace('%', ''), + 'start': start, + 'page_length': frappe.utils.cint(page_length), + }, as_dict=as_dict) From cb38cc96c36d441ccc06b1d6421b48405b24045e Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Tue, 20 Aug 2019 15:00:48 +0530 Subject: [PATCH 27/79] fix: Enhancement in Purchase Order --- .../buying_settings/buying_settings.js | 8 + .../buying_settings/buying_settings.json | 468 ++++------------ .../buying_settings/test_buying_settings.py | 10 + .../doctype/purchase_order/purchase_order.py | 1 + .../purchase_order_item_supplied.json | 508 ++++-------------- .../stock/doctype/stock_entry/stock_entry.py | 15 +- .../stock_entry_detail.json | 12 +- 7 files changed, 262 insertions(+), 760 deletions(-) create mode 100644 erpnext/buying/doctype/buying_settings/buying_settings.js create mode 100644 erpnext/buying/doctype/buying_settings/test_buying_settings.py diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js new file mode 100644 index 0000000000..403b1c95e7 --- /dev/null +++ b/erpnext/buying/doctype/buying_settings/buying_settings.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('Buying Settings', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index f2eec4f722..a492519591 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -1,379 +1,111 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-06-25 11:04:03", - "custom": 0, - "description": "Settings for Buying Module", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Other", - "editable_grid": 0, + "creation": "2013-06-25 11:04:03", + "description": "Settings for Buying Module", + "doctype": "DocType", + "document_type": "Other", + "field_order": [ + "supp_master_name", + "supplier_group", + "buying_price_list", + "column_break_3", + "po_required", + "pr_required", + "maintain_same_rate", + "allow_multiple_items", + "subcontract", + "backflush_raw_materials_of_subcontract_based_on", + "column_break_11", + "over_transfer_allowance" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Supplier Name", - "fieldname": "supp_master_name", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Supplier Naming By", - "length": 0, - "no_copy": 0, - "options": "Supplier Name\nNaming Series", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "supplier_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Supplier Group", - "length": 0, - "no_copy": 0, - "options": "Supplier Group", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "buying_price_list", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Buying Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "po_required", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Purchase Order Required", - "length": 0, - "no_copy": 0, - "options": "No\nYes", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "pr_required", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Purchase Receipt Required", - "length": 0, - "no_copy": 0, - "options": "No\nYes", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintain_same_rate", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintain same rate throughout purchase cycle", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "allow_multiple_items", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Item to be added multiple times in a transaction", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "Supplier Name", + "fieldname": "supp_master_name", + "fieldtype": "Select", + "label": "Supplier Naming By", + "options": "Supplier Name\nNaming Series" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subcontract", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subcontract", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "supplier_group", + "fieldtype": "Link", + "label": "Default Supplier Group", + "options": "Supplier Group" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Material Transferred for Subcontract", - "fieldname": "backflush_raw_materials_of_subcontract_based_on", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Backflush Raw Materials of Subcontract Based On", - "length": 0, - "no_copy": 0, - "options": "BOM\nMaterial Transferred for Subcontract", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "buying_price_list", + "fieldtype": "Link", + "label": "Default Buying Price List", + "options": "Price List" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "po_required", + "fieldtype": "Select", + "label": "Purchase Order Required", + "options": "No\nYes" + }, + { + "fieldname": "pr_required", + "fieldtype": "Select", + "label": "Purchase Receipt Required", + "options": "No\nYes" + }, + { + "default": "0", + "fieldname": "maintain_same_rate", + "fieldtype": "Check", + "label": "Maintain same rate throughout purchase cycle" + }, + { + "default": "0", + "fieldname": "allow_multiple_items", + "fieldtype": "Check", + "label": "Allow Item to be added multiple times in a transaction" + }, + { + "fieldname": "subcontract", + "fieldtype": "Section Break", + "label": "Subcontract" + }, + { + "default": "Material Transferred for Subcontract", + "fieldname": "backflush_raw_materials_of_subcontract_based_on", + "fieldtype": "Select", + "label": "Backflush Raw Materials of Subcontract Based On", + "options": "BOM\nMaterial Transferred for Subcontract" + }, + { + "depends_on": "eval:doc.backflush_raw_materials_of_subcontract_based_on == \"BOM\"", + "description": "Percentage you are allowed to transfer more against the quantity ordered. For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to transfer 110 units.", + "fieldname": "over_transfer_allowance", + "fieldtype": "Float", + "label": "Over Transfer Allowance (%)" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-cog", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-31 07:52:38.062488", - "modified_by": "Administrator", - "module": "Buying", - "name": "Buying Settings", - "owner": "Administrator", + ], + "icon": "fa fa-cog", + "idx": 1, + "issingle": 1, + "modified": "2019-08-20 13:13:09.055189", + "modified_by": "Administrator", + "module": "Buying", + "name": "Buying Settings", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "track_changes": 0, - "track_seen": 0 -} + ] +} \ No newline at end of file diff --git a/erpnext/buying/doctype/buying_settings/test_buying_settings.py b/erpnext/buying/doctype/buying_settings/test_buying_settings.py new file mode 100644 index 0000000000..bf6eec67d4 --- /dev/null +++ b/erpnext/buying/doctype/buying_settings/test_buying_settings.py @@ -0,0 +1,10 @@ +# -*- 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 TestBuyingSettings(unittest.TestCase): + pass diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 8117d9d514..4bd172b514 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -477,6 +477,7 @@ def make_rm_stock_entry(purchase_order, rm_items): rm_item_code = rm_item_data["rm_item_code"] items_dict = { rm_item_code: { + "po_detail": rm_item_data["name"], "item_name": rm_item_data["item_name"], "description": item_wh.get(rm_item_code, {}).get('description', ""), 'qty': rm_item_data["qty"], diff --git a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json index 7016853058..8435bbb06e 100644 --- a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json +++ b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json @@ -1,404 +1,134 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-02-22 01:27:42", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, + "creation": "2013-02-22 01:27:42", + "doctype": "DocType", + "editable_grid": 1, + "field_order": [ + "main_item_code", + "rm_item_code", + "required_qty", + "supplied_qty", + "rate", + "amount", + "column_break_6", + "bom_detail_no", + "reference_name", + "conversion_factor", + "stock_uom", + "reserve_warehouse" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "main_item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "oldfieldname": "main_item_code", - "oldfieldtype": "Data", - "options": "Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "main_item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "oldfieldname": "main_item_code", + "oldfieldtype": "Data", + "options": "Item", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "rm_item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Raw Material Item Code", - "length": 0, - "no_copy": 0, - "oldfieldname": "rm_item_code", - "oldfieldtype": "Data", - "options": "Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "rm_item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Raw Material Item Code", + "oldfieldname": "rm_item_code", + "oldfieldtype": "Data", + "options": "Item", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "required_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Supplied Qty", - "length": 0, - "no_copy": 0, - "oldfieldname": "required_qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "required_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Required Qty", + "oldfieldname": "required_qty", + "oldfieldtype": "Currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "rate", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Rate", - "length": 0, - "no_copy": 0, - "oldfieldname": "rate", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Rate", + "oldfieldname": "rate", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "oldfieldname": "amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount", + "oldfieldname": "amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bom_detail_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "BOM Detail No", - "length": 0, - "no_copy": 0, - "oldfieldname": "bom_detail_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bom_detail_no", + "fieldtype": "Data", + "label": "BOM Detail No", + "oldfieldname": "bom_detail_no", + "oldfieldtype": "Data", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "reference_name", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_name", + "fieldtype": "Data", + "label": "Reference Name", + "oldfieldname": "reference_name", + "oldfieldtype": "Data", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "conversion_factor", - "fieldtype": "Float", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Conversion Factor", - "length": 0, - "no_copy": 0, - "oldfieldname": "conversion_factor", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "conversion_factor", + "fieldtype": "Float", + "hidden": 1, + "label": "Conversion Factor", + "oldfieldname": "conversion_factor", + "oldfieldtype": "Currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stock_uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Stock Uom", - "length": 0, - "no_copy": 0, - "oldfieldname": "stock_uom", - "oldfieldtype": "Data", - "options": "UOM", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock Uom", + "oldfieldname": "stock_uom", + "oldfieldtype": "Data", + "options": "UOM", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "reserve_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reserve Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "columns": 2, + "fieldname": "reserve_warehouse", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reserve Warehouse", + "options": "Warehouse" + }, + { + "fieldname": "supplied_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Supplied Qty", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 1, - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-01-07 16:51:58.016007", - "modified_by": "Administrator", - "module": "Buying", - "name": "Purchase Order Item Supplied", - "owner": "dhanalekshmi@webnotestech.com", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "hide_toolbar": 1, + "idx": 1, + "istable": 1, + "modified": "2019-08-20 13:37:32.702068", + "modified_by": "Administrator", + "module": "Buying", + "name": "Purchase Order Item Supplied", + "owner": "dhanalekshmi@webnotestech.com", + "permissions": [] } \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index b5303327f6..32a785f258 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -521,14 +521,20 @@ class StockEntry(StockController): backflush_raw_materials_based_on = frappe.db.get_single_value("Buying Settings", "backflush_raw_materials_of_subcontract_based_on") + qty_allowance = flt(frappe.db.get_single_value("Buying Settings", + "over_transfer_allowance")) + if (self.purpose == "Send to Subcontractor" and self.purchase_order and backflush_raw_materials_based_on == 'BOM'): purchase_order = frappe.get_doc("Purchase Order", self.purchase_order) for se_item in self.items: item_code = se_item.original_item or se_item.item_code precision = cint(frappe.db.get_default("float_precision")) or 3 - total_allowed = sum([flt(d.required_qty) for d in purchase_order.supplied_items \ + required_qty = sum([flt(d.required_qty) for d in purchase_order.supplied_items \ if d.rm_item_code == item_code]) + + total_allowed = required_qty + (required_qty * (qty_allowance/100)) + if not total_allowed: frappe.throw(_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}") .format(se_item.item_code, self.purchase_order)) @@ -1106,6 +1112,7 @@ class StockEntry(StockController): se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0) se_child.subcontracted_item = item_dict[d].get("main_item_code") se_child.original_item = item_dict[d].get("original_item") + se_child.po_detail = item_dict[d].get("po_detail") if item_dict[d].get("idx"): se_child.idx = item_dict[d].get("idx") @@ -1157,7 +1164,11 @@ class StockEntry(StockController): where po.name = poitemsup.parent and po.name = %s""", self.purchase_order)) - #Update reserved sub contracted quantity in bin based on Supplied Item Details + #Update Supplied Qty in PO Supplied Items + frappe.db.sql("""UPDATE `tabPurchase Order Item Supplied` pos, `tabStock Entry Detail` sed + SET pos.supplied_qty = sed.transfer_qty where pos.name = sed.po_detail""") + + #Update reserved sub contracted quantity in bin based on Supplied Item Details and for d in self.get("items"): item_code = d.get('original_item') or d.get('item_code') reserve_warehouse = item_wh.get(item_code) diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 912a4651b3..d86e68b722 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -59,6 +59,7 @@ "reference_section", "against_stock_entry", "ste_detail", + "po_detail", "column_break_51", "transferred_qty", "reference_purchase_receipt", @@ -480,11 +481,20 @@ "label": "Project", "options": "Project", "read_only": 1 + }, + { + "fieldname": "po_detail", + "fieldtype": "Data", + "hidden": 1, + "label": "PO Supplied Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2019-07-12 11:34:53.190749", + "modified": "2019-08-20 14:01:02.319754", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", From 9e6abc49481f1749876b33a70e5bea8509f5f9ec Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Tue, 20 Aug 2019 16:14:44 +0530 Subject: [PATCH 28/79] fix: Test case fixes --- erpnext/buying/doctype/purchase_order/purchase_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 4bd172b514..8ad320ee47 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -477,7 +477,7 @@ def make_rm_stock_entry(purchase_order, rm_items): rm_item_code = rm_item_data["rm_item_code"] items_dict = { rm_item_code: { - "po_detail": rm_item_data["name"], + "po_detail": rm_item_data.get("name"), "item_name": rm_item_data["item_name"], "description": item_wh.get(rm_item_code, {}).get('description', ""), 'qty': rm_item_data["qty"], From d79a16c9f3c33faeb14ea35aefcf71db5f1f9ae9 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 21 Aug 2019 09:03:26 +0530 Subject: [PATCH 29/79] 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 --- .../doctype/work_order/work_order.py | 2 +- erpnext/stock/doctype/pick_list/pick_list.js | 61 ++++++++------- .../stock/doctype/pick_list/pick_list.json | 63 ++++++++++----- erpnext/stock/doctype/pick_list/pick_list.py | 76 +++++++++++-------- .../stock/doctype/pick_list/test_pick_list.py | 6 +- 5 files changed, 126 insertions(+), 82 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index c653deece3..988cd223f2 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -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 diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index b9b317b732..bd893aafd0 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -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 }); }); } diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 70272bf584..1a33622167 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -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", diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 91bdcb33ee..8699ee6e19 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -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) diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index ed4b6b58cc..4048e5d0b0 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -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, From 298d38cde3c8a5d74ee9ddb61464f4911f70ee92 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 21 Aug 2019 16:37:49 +0530 Subject: [PATCH 30/79] test: Fix existing tests --- .../stock/doctype/pick_list/test_pick_list.py | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index 4048e5d0b0..df42ff1256 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -14,37 +14,44 @@ class TestPickList(unittest.TestCase): pick_list = frappe.get_doc({ 'doctype': 'Pick List', 'company': '_Test Company', + 'customer': '_Test Customer', + 'items_based_on': 'Sales Order', 'items': [{ - 'item': '_Test Item Home Desktop 100', - 'reference_doctype': 'Sales Order', + 'item_code': '_Test Item Home Desktop 100', 'qty': 5, - 'reference_name': '_T-Sales Order-1', - }], + 'stock_qty': 5, + 'conversion_factor': 1, + 'sales_order': '_T-Sales Order-1', + 'sales_item': '_T-Sales Order-1_item', + }] }) - pick_list.set_item_locations() - self.assertEqual(pick_list.items_locations[0].item, '_Test Item Home Desktop 100') - self.assertEqual(pick_list.items_locations[0].warehouse, '_Test Warehouse - _TC') - self.assertEqual(pick_list.items_locations[0].qty, 5) + self.assertEqual(pick_list.locations[0].item_code, '_Test Item Home Desktop 100') + self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_list.locations[0].qty, 5) def test_pick_list_skips_out_of_stock_item(self): pick_list = frappe.get_doc({ 'doctype': 'Pick List', 'company': '_Test Company', + 'customer': '_Test Customer', + 'items_based_on': 'Sales Order', 'items': [{ - 'item': '_Test Item Warehouse Group Wise Reorder', - 'reference_doctype': 'Sales Order', + 'item_code': '_Test Item Warehouse Group Wise Reorder', 'qty': 1000, - 'reference_name': '_T-Sales Order-1', - }], + 'stock_qty': 1000, + 'conversion_factor': 1, + 'sales_order': '_T-Sales Order-1', + 'sales_item': '_T-Sales Order-1_item', + }] }) pick_list.set_item_locations() - self.assertEqual(pick_list.items_locations[0].item, '_Test Item Warehouse Group Wise Reorder') - self.assertEqual(pick_list.items_locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') - self.assertEqual(pick_list.items_locations[0].qty, 30) + self.assertEqual(pick_list.locations[0].item_code, '_Test Item Warehouse Group Wise Reorder') + self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') + self.assertEqual(pick_list.locations[0].qty, 30) def test_pick_list_skips_items_in_expired_batch(self): @@ -68,19 +75,23 @@ class TestPickList(unittest.TestCase): pick_list = frappe.get_doc({ 'doctype': 'Pick List', 'company': '_Test Company', + 'customer': '_Test Customer', + 'items_based_on': 'Sales Order', 'items': [{ - 'item': '_Test Serialized Item', - 'reference_doctype': 'Sales Order', + 'item_code': '_Test Serialized Item', 'qty': 1000, - 'reference_name': '_T-Sales Order-1', - }], + 'stock_qty': 1000, + 'conversion_factor': 1, + 'sales_order': '_T-Sales Order-1', + 'sales_item': '_T-Sales Order-1_item', + }] }) pick_list.set_item_locations() - self.assertEqual(pick_list.items_locations[0].item, '_Test Serialized Item') - self.assertEqual(pick_list.items_locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') - self.assertEqual(pick_list.items_locations[0].qty, 30) - self.assertEqual(pick_list.items_locations[0].serial_no, 30) + self.assertEqual(pick_list.locations[0].item_code, '_Test Serialized Item') + self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_list.locations[0].qty, 5) + self.assertEqual(pick_list.locations[0].serial_no, '123450\n123451\n123452\n123453\n123454') def test_pick_list_for_multiple_reference_doctypes(self): From ec92486377fa7e8a1a56ca65129d00f2bec4220a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 21 Aug 2019 22:10:27 +0530 Subject: [PATCH 31/79] fix: Add option to create Stock Entry from Pick List --- erpnext/stock/doctype/pick_list/pick_list.js | 22 ++++- erpnext/stock/doctype/pick_list/pick_list.py | 88 ++++++++++++++------ 2 files changed, 80 insertions(+), 30 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index bd893aafd0..08a388872d 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -28,8 +28,11 @@ frappe.ui.form.on('Pick List', { frm.call('set_item_locations'); }).addClass('btn-primary'); } - - frm.add_custom_button(__('Delivery Note'), () => frm.trigger('make_delivery_note'), __('Create')); + if (frm.doc.items_based_on === 'Sales Order') { + 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')); + } }, work_order: (frm) => { frm.clear_table('items'); @@ -42,12 +45,23 @@ frappe.ui.form.on('Pick List', { items_based_on: (frm) => { frm.trigger('add_get_items_button'); }, - make_delivery_note(frm) { + create_delivery_note(frm) { frappe.model.open_mapped_doc({ - method: 'erpnext.stock.doctype.pick_list.pick_list.make_delivery_note', + method: 'erpnext.stock.doctype.pick_list.pick_list.create_delivery_note', frm: frm }); }, + create_stock_entry(frm) { + // TODO: show dialog for qty + + frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.create_stock_entry', { + 'pick_list': frm.doc, + 'qty': 1 + }).then(stock_entry => { + frappe.model.sync(stock_entry); + frappe.set_route("Form", 'Stock Entry', stock_entry.name); + }); + }, add_get_items_button(frm) { let source_doctype = frm.doc.items_based_on; if (source_doctype != 'Sales Order') return; diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 8699ee6e19..34f1ab5895 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -4,11 +4,12 @@ from __future__ import unicode_literals import frappe -from frappe.model.document import Document +import json from six import iteritems -from frappe.model.mapper import get_mapped_doc, map_child_doc +from frappe.model.document import Document from frappe.utils import floor, flt, today -from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note as make_delivery_note_from_sales_order +from frappe.model.mapper import get_mapped_doc, map_child_doc +from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note as create_delivery_note_from_sales_order # TODO: Prioritize SO or WO group warehouse @@ -49,7 +50,6 @@ class PickList(Document): 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 available_locations: @@ -98,15 +98,6 @@ def get_available_items(item_code, from_warehouses): return available_items -def set_serial_nos(item_doc): - serial_nos = frappe.get_all('Serial No', { - 'item_code': item_doc.item_code, - 'warehouse': item_doc.warehouse - }, limit=item_doc.stock_qty, order_by='purchase_date') - item_doc.set('serial_no', '\n'.join([serial_no.name for serial_no in serial_nos])) - - # should we assume that all serialized item_code available in stock will have serial no? - def get_item_locations_based_on_serial_nos(item_doc): serial_nos = frappe.get_all('Serial No', fields = ['name', 'warehouse'], @@ -176,26 +167,26 @@ def get_item_locations_based_on_batch_nos(item_doc): return locations @frappe.whitelist() -def make_delivery_note(source_name, target_doc=None): +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 = set(sales_orders) delivery_note = None for sales_order in sales_orders: - delivery_note = make_delivery_note_from_sales_order(sales_order, + delivery_note = create_delivery_note_from_sales_order(sales_order, delivery_note, skip_item_mapping=True) 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", - "field_map": { - "rate": "rate", - "name": "so_detail", - "parent": "against_sales_order", + 'doctype': 'Delivery Note Item', + 'field_map': { + 'rate': 'rate', + 'name': 'so_detail', + 'parent': 'against_sales_order', }, - "condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 + 'condition': lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 } dn_item = map_child_doc(sales_order_item, delivery_note, item_table_mapper) @@ -211,11 +202,6 @@ def make_delivery_note(source_name, target_doc=None): return delivery_note -def set_delivery_note_missing_values(target): - target.run_method("set_missing_values") - target.run_method("set_po_nos") - target.run_method("calculate_taxes_and_totals") - def update_delivery_note_item(source, target, delivery_note): cost_center = frappe.db.get_value("Project", delivery_note.project, "cost_center") if not cost_center: @@ -238,6 +224,56 @@ def update_delivery_note_item(source, target, delivery_note): target.cost_center = cost_center +def set_delivery_note_missing_values(target): + target.run_method('set_missing_values') + target.run_method('set_po_nos') + target.run_method('calculate_taxes_and_totals') + + +@frappe.whitelist() +def create_stock_entry(pick_list, qty): + pick_list = frappe.get_doc(json.loads(pick_list)) + work_order = frappe.get_doc("Work Order", pick_list.get('work_order')) + if not qty: + qty = work_order.qty - work_order.material_transferred_for_manufacturing + if not qty: return + + stock_entry = frappe.new_doc('Stock Entry') + stock_entry.purpose = 'Material Transfer For Manufacture' + stock_entry.set_stock_entry_type() + stock_entry.work_order = work_order.name + stock_entry.company = work_order.company + stock_entry.from_bom = 1 + stock_entry.bom_no = work_order.bom_no + stock_entry.use_multi_level_bom = work_order.use_multi_level_bom + stock_entry.fg_completed_qty = (flt(work_order.qty) - flt(work_order.produced_qty)) + if work_order.bom_no: + stock_entry.inspection_required = frappe.db.get_value('BOM', + work_order.bom_no, 'inspection_required') + + is_wip_warehouse_group = frappe.db.get_value('Warehouse', work_order.wip_warehouse, 'is_group') + if not (is_wip_warehouse_group and work_order.skip_transfer): + wip_warehouse = work_order.wip_warehouse + else: + wip_warehouse = None + stock_entry.to_warehouse = wip_warehouse + + stock_entry.project = work_order.project + + for location in pick_list.locations: + item = frappe._dict() + item.item_code = location.item_code + item.s_warehouse = location.warehouse + item.t_warehouse = wip_warehouse + item.qty = location.qty + item.uom = location.uom + item.conversion_factor = location.conversion_factor + item.stock_uom = location.stock_uom + + stock_entry.append('items', item) + + return stock_entry.as_dict() + @frappe.whitelist() def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filters, as_dict): return frappe.db.sql(""" From c1f25ff9e302b2f0036d6394175b9cf8519cd496 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 22 Aug 2019 09:53:44 +0530 Subject: [PATCH 32/79] fix: Add option to select qty of finished goods Item --- erpnext/stock/doctype/pick_list/pick_list.js | 31 ++++++++++++++----- .../stock/doctype/pick_list/pick_list.json | 13 ++++++-- erpnext/stock/doctype/pick_list/pick_list.py | 7 ++--- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 08a388872d..e3064fe757 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -36,10 +36,30 @@ frappe.ui.form.on('Pick List', { }, 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 + frappe.db.get_value('Work Order', + frm.doc.work_order, + ['qty', 'produced_qty'] + ).then(data => { + let qty_data = data.message; + let max = qty_data.qty - qty_data.produced_qty; + frappe.prompt({ + fieldtype: 'Float', + label: __('Qty'), + fieldname: 'qty', + description: __('Max: {0}', [max]), + default: max + }, (data) => { + frm.set_value('qty', data.qty); + if (data.qty > max) { + frappe.msgprint(__('Quantity must not be more than {0}', [max])); + return; + } + erpnext.utils.map_current_doc({ + method: 'erpnext.manufacturing.doctype.work_order.work_order.create_pick_list', + target: frm, + source_name: frm.doc.work_order + }); + }, __("Select Quantity"), __('Get Items')); }); }, items_based_on: (frm) => { @@ -52,11 +72,8 @@ frappe.ui.form.on('Pick List', { }); }, create_stock_entry(frm) { - // TODO: show dialog for qty - frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.create_stock_entry', { 'pick_list': frm.doc, - 'qty': 1 }).then(stock_entry => { frappe.model.sync(stock_entry); frappe.set_route("Form", 'Stock Entry', stock_entry.name); diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 1a33622167..e321a0bf5b 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -8,6 +8,7 @@ "items_based_on", "customer", "work_order", + "qty", "column_break_4", "parent_warehouse", "company", @@ -59,8 +60,7 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Customer", - "options": "Customer", - "reqd": 1 + "options": "Customer" }, { "depends_on": "eval:doc.items_based_on===\"Work Order\"", @@ -81,9 +81,16 @@ "fieldtype": "Table", "label": "Item Locations", "options": "Pick List Item" + }, + { + "depends_on": "work_order", + "description": "Qty of raw materials will be decided based on the qty of the Finished Goods Item", + "fieldname": "qty", + "fieldtype": "Float", + "label": "Qty of Finished Goods Item" } ], - "modified": "2019-08-20 16:57:11.006221", + "modified": "2019-08-22 09:50:01.099449", "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 34f1ab5895..d7f420d8f6 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -231,12 +231,9 @@ def set_delivery_note_missing_values(target): @frappe.whitelist() -def create_stock_entry(pick_list, qty): +def create_stock_entry(pick_list): pick_list = frappe.get_doc(json.loads(pick_list)) work_order = frappe.get_doc("Work Order", pick_list.get('work_order')) - if not qty: - qty = work_order.qty - work_order.material_transferred_for_manufacturing - if not qty: return stock_entry = frappe.new_doc('Stock Entry') stock_entry.purpose = 'Material Transfer For Manufacture' @@ -246,7 +243,7 @@ def create_stock_entry(pick_list, qty): stock_entry.from_bom = 1 stock_entry.bom_no = work_order.bom_no stock_entry.use_multi_level_bom = work_order.use_multi_level_bom - stock_entry.fg_completed_qty = (flt(work_order.qty) - flt(work_order.produced_qty)) + stock_entry.fg_completed_qty = pick_list.qty if work_order.bom_no: stock_entry.inspection_required = frappe.db.get_value('BOM', work_order.bom_no, 'inspection_required') From 8d55f81baa26df3da82bda179abadbb6f4c95c6d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 22 Aug 2019 16:37:44 +0530 Subject: [PATCH 33/79] fix: Fix raw matterial selection based on finished item qty --- .../doctype/work_order/work_order.py | 43 ++++++++++++++++--- erpnext/stock/doctype/pick_list/pick_list.js | 33 ++++++++++---- .../stock/doctype/pick_list/pick_list.json | 4 +- erpnext/stock/doctype/pick_list/pick_list.py | 3 +- 4 files changed, 64 insertions(+), 19 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 988cd223f2..804d22ce6b 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -711,13 +711,29 @@ def get_work_order_operation_data(work_order, operation, workstation): @frappe.whitelist() def create_pick_list(source_name, target_doc=None): + pick_list = json.loads(target_doc) + max_finished_goods_qty = frappe.db.get_value('Work Order', source_name, 'qty') def update_item_quantity(source, target, source_parent): - qty = source.required_qty - source.transferred_qty - target.qty = qty - target.stock_qty = qty - target.uom = frappe.get_value('Item', source.item_code, 'stock_uom') - target.stock_uom = target.uom - target.conversion_factor = 1 + # qty = source.required_qty - source.transferred_qty + # target.qty = qty + + pending_to_issue = flt(source.required_qty) - flt(source.transferred_qty) + desire_to_transfer = flt(source.required_qty) / max_finished_goods_qty * flt(pick_list.get('for_qty')) + + qty = 0 + if desire_to_transfer <= pending_to_issue: + qty = desire_to_transfer + elif pending_to_issue > 0: + qty = pending_to_issue + + if qty: + target.qty = qty + target.stock_qty = qty + target.uom = frappe.get_value('Item', source.item_code, 'stock_uom') + target.stock_uom = target.uom + target.conversion_factor = 1 + else: + target.delete() doc = get_mapped_doc("Work Order", source_name, { "Work Order": { @@ -733,4 +749,17 @@ def create_pick_list(source_name, target_doc=None): }, }, target_doc) - return doc + # # aggregate qty for same item + # item_map = frappe._dict() + # for item in doc.items: + # item.idx = None + # if not item_map.get(item.item_code): + # item_map[item.item_code] = item + # else: + # item_map[item.item_code].qty += item.qty + # item_map[item.item_code].stock_qty += item.stock_qty + + # doc.delete_key('items') + # doc.set('items', item_map.values()) + + return doc \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index e3064fe757..6f39d88a6b 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -28,32 +28,35 @@ frappe.ui.form.on('Pick List', { frm.call('set_item_locations'); }).addClass('btn-primary'); } - if (frm.doc.items_based_on === 'Sales Order') { - 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')); + if (frm.doc.docstatus == 1) { + if (frm.doc.items_based_on === 'Sales Order') { + 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')); + } } }, work_order: (frm) => { - frm.clear_table('items'); frappe.db.get_value('Work Order', frm.doc.work_order, - ['qty', 'produced_qty'] + ['qty', 'material_transferred_for_manufacturing'] ).then(data => { let qty_data = data.message; - let max = qty_data.qty - qty_data.produced_qty; + let max = qty_data.qty - qty_data.material_transferred_for_manufacturing; frappe.prompt({ fieldtype: 'Float', - label: __('Qty'), + label: __('Qty of Finished Goods Item'), fieldname: 'qty', description: __('Max: {0}', [max]), default: max }, (data) => { - frm.set_value('qty', data.qty); + frm.set_value('for_qty', data.qty); if (data.qty > max) { frappe.msgprint(__('Quantity must not be more than {0}', [max])); return; } + frm.clear_table('items'); + frm.clear_table('locations'); erpnext.utils.map_current_doc({ method: 'erpnext.manufacturing.doctype.work_order.work_order.create_pick_list', target: frm, @@ -63,6 +66,8 @@ frappe.ui.form.on('Pick List', { }); }, items_based_on: (frm) => { + frm.clear_table('items'); + frm.clear_table('locations'); frm.trigger('add_get_items_button'); }, create_delivery_note(frm) { @@ -107,3 +112,13 @@ frappe.ui.form.on('Pick List', { }); } }); + + +// frappe.ui.form.on('Pick List Reference Item', { +// item_code: (frm, cdt, cdn) => { +// let row = locals[cdt][cdn]; +// if (row.item_code) { +// frappe.xcall(''); +// } +// } +// }); \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index e321a0bf5b..0fd7dfbfc2 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -8,7 +8,7 @@ "items_based_on", "customer", "work_order", - "qty", + "for_qty", "column_break_4", "parent_warehouse", "company", @@ -85,7 +85,7 @@ { "depends_on": "work_order", "description": "Qty of raw materials will be decided based on the qty of the Finished Goods Item", - "fieldname": "qty", + "fieldname": "for_qty", "fieldtype": "Float", "label": "Qty of Finished Goods Item" } diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index d7f420d8f6..f69fbd53b3 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -61,6 +61,7 @@ def get_items_with_warehouse_and_quantity(item_doc, from_warehouses, item_locati if uom_must_be_whole_number: qty = floor(qty) stock_qty = qty * item_doc.conversion_factor + if not stock_qty: break locations.append({ 'qty': qty, @@ -243,7 +244,7 @@ def create_stock_entry(pick_list): stock_entry.from_bom = 1 stock_entry.bom_no = work_order.bom_no stock_entry.use_multi_level_bom = work_order.use_multi_level_bom - stock_entry.fg_completed_qty = pick_list.qty + stock_entry.fg_completed_qty = pick_list.for_qty if work_order.bom_no: stock_entry.inspection_required = frappe.db.get_value('BOM', work_order.bom_no, 'inspection_required') From 687b1a5b1f9bed8a763fc037dcb9ead4645b0983 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 22 Aug 2019 16:41:14 +0530 Subject: [PATCH 34/79] fix: Make pick list submittable and add few fields - Add material request & item fields - Add pick_list field to Stock Entry - Add purpose field to pick list --- .../stock/doctype/pick_list/pick_list.json | 76 ++++++++++++++++++- erpnext/stock/doctype/pick_list/pick_list.py | 11 ++- .../pick_list_reference_item.json | 23 +++++- .../doctype/stock_entry/stock_entry.json | 11 ++- 4 files changed, 111 insertions(+), 10 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 0fd7dfbfc2..504a3fa14f 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -5,6 +5,7 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "purpose", "items_based_on", "customer", "work_order", @@ -15,7 +16,8 @@ "section_break_4", "items", "section_break_6", - "locations" + "locations", + "amended_from" ], "fields": [ { @@ -87,16 +89,36 @@ "description": "Qty of raw materials will be decided based on the qty of the Finished Goods Item", "fieldname": "for_qty", "fieldtype": "Float", - "label": "Qty of Finished Goods Item" + "label": "Qty of Finished Goods Item", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Pick List", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "Material Transfer for manufacturing", + "fieldname": "purpose", + "fieldtype": "Select", + "label": "Purpose", + "options": "Material Transfer for manufacturing\nMaterial Issue\nMaterial Transfer\nDelivery against Sales Order" } ], - "modified": "2019-08-22 09:50:01.099449", + "is_submittable": 1, + "modified": "2019-08-22 13:36:18.912659", "modified_by": "Administrator", "module": "Stock", "name": "Pick List", "owner": "Administrator", "permissions": [ { + "amend": 1, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -104,8 +126,54 @@ "print": 1, "read": 1, "report": 1, - "role": "System Manager", + "role": "Stock Manager", "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "submit": 1, "write": 1 } ], diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index f69fbd53b3..550c70f2d1 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -237,6 +237,7 @@ def create_stock_entry(pick_list): work_order = frappe.get_doc("Work Order", pick_list.get('work_order')) stock_entry = frappe.new_doc('Stock Entry') + stock_entry.pick_list = pick_list.get('name') stock_entry.purpose = 'Material Transfer For Manufacture' stock_entry.set_stock_entry_type() stock_entry.work_order = work_order.name @@ -264,12 +265,17 @@ def create_stock_entry(pick_list): item.s_warehouse = location.warehouse item.t_warehouse = wip_warehouse item.qty = location.qty + item.transfer_qty = location.stock_qty item.uom = location.uom item.conversion_factor = location.conversion_factor item.stock_uom = location.stock_uom stock_entry.append('items', item) + stock_entry.set_incoming_rate() + stock_entry.set_actual_qty() + stock_entry.calculate_rate_and_amount(update_finished_item_rate=False) + return stock_entry.as_dict() @frappe.whitelist() @@ -281,7 +287,7 @@ def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filte `tabWork Order` WHERE `status` not in ('Completed', 'Stopped') - AND `qty` > `produced_qty` + AND `qty` > `material_transferred_for_manufacturing` AND `docstatus` = 1 AND `company` = %(company)s AND `name` like %(txt)s @@ -296,3 +302,6 @@ def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filte 'page_length': frappe.utils.cint(page_length), 'company': filters.get('company') }, as_dict=as_dict) + +def get_item_details(item_code): + pass \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json index 0aa94feb37..e6984ef323 100644 --- a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json +++ b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json @@ -14,7 +14,9 @@ "conversion_factor", "reference_section", "sales_order", - "sales_order_item" + "sales_order_item", + "material_request", + "material_request_item" ], "fields": [ { @@ -65,21 +67,34 @@ "fieldname": "sales_order", "fieldtype": "Link", "label": "Sales Order", - "options": "Sales Order" + "options": "Sales Order", + "read_only": 1 }, { "fieldname": "sales_order_item", "fieldtype": "Data", - "label": "Sales Order Item" + "label": "Sales Order Item", + "read_only": 1 }, { "fieldname": "conversion_factor", "fieldtype": "Float", "label": "UOM Conversion Factor" + }, + { + "fieldname": "material_request", + "fieldtype": "Link", + "label": "Material Request", + "options": "Material Request" + }, + { + "fieldname": "material_request_item", + "fieldtype": "Data", + "label": "Material Request Item" } ], "istable": 1, - "modified": "2019-08-14 18:38:28.867113", + "modified": "2019-08-22 14:19:19.725540", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Reference Item", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index ee563cb4a0..22b84a18dd 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -17,6 +17,7 @@ "purchase_order", "delivery_note_no", "sales_invoice_no", + "pick_list", "purchase_receipt_no", "col2", "posting_date", @@ -613,12 +614,20 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "pick_list", + "fieldtype": "Link", + "hidden": 1, + "label": "Pick List", + "options": "Pick List", + "read_only": 1 } ], "icon": "fa fa-file-text", "idx": 1, "is_submittable": 1, - "modified": "2019-07-14 17:41:39.257508", + "modified": "2019-08-22 13:09:55.344036", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", From f2798eab502231ae5d0dce0bd26caa690baffc6b Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Fri, 23 Aug 2019 10:07:52 +0530 Subject: [PATCH 35/79] fix: Conside multiple stock entry against a purchase order --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 32a785f258..e04d4c9512 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -535,7 +535,7 @@ class StockEntry(StockController): total_allowed = required_qty + (required_qty * (qty_allowance/100)) - if not total_allowed: + if not required_qty: frappe.throw(_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}") .format(se_item.item_code, self.purchase_order)) total_supplied = frappe.db.sql("""select sum(transfer_qty) From 8b4d604cfda01e1a67503d10ce7ce175fed51f8a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 23 Aug 2019 11:57:16 +0530 Subject: [PATCH 36/79] fix: Use purpose field and remove item_base_on field --- .../doctype/sales_order/sales_order.py | 5 ++--- erpnext/stock/doctype/pick_list/pick_list.js | 19 +++++++++++------- .../stock/doctype/pick_list/pick_list.json | 20 +++++-------------- erpnext/stock/doctype/pick_list/pick_list.py | 14 +++++++++++-- .../doctype/stock_entry/stock_entry.json | 3 +-- 5 files changed, 32 insertions(+), 29 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index dd156d0473..426b00484c 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1010,9 +1010,6 @@ def make_pick_list(source_name, target_doc=None): doc = get_mapped_doc("Sales Order", source_name, { "Sales Order": { "doctype": "Pick List", - "field_map": { - "doctype": "items_based_on" - }, "validation": { "docstatus": ["=", 1] } @@ -1028,4 +1025,6 @@ def make_pick_list(source_name, target_doc=None): }, }, target_doc) + doc.purpose = 'Delivery against Sales Order' + return doc diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 6f39d88a6b..c2dddab4c9 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -23,16 +23,21 @@ frappe.ui.form.on('Pick List', { refresh: (frm) => { frm.trigger('add_get_items_button'); - if (frm.doc.items && (frm.doc.items.length > 1 || frm.doc.items[0].item_code)) { + if (frm.doc.items && (frm.doc.items.length > 1 || frm.doc.items[0].item_code) && frm.doc.docstatus === 0) { frm.add_custom_button(__('Get Item Locations'), () => { frm.call('set_item_locations'); }).addClass('btn-primary'); } - if (frm.doc.docstatus == 1) { - if (frm.doc.items_based_on === 'Sales Order') { + if (frm.doc.docstatus === 1) { + if (frm.doc.purpose === 'Delivery against Sales Order') { 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')); + frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.stock_entry_exists', { + 'pick_list_name': frm.doc.name + }).then(exists => { + if (exists) return; + frm.add_custom_button(__('Stock Entry'), () => frm.trigger('create_stock_entry'), __('Create')); + }); } } }, @@ -65,7 +70,7 @@ frappe.ui.form.on('Pick List', { }, __("Select Quantity"), __('Get Items')); }); }, - items_based_on: (frm) => { + purpose: (frm) => { frm.clear_table('items'); frm.clear_table('locations'); frm.trigger('add_get_items_button'); @@ -85,8 +90,8 @@ frappe.ui.form.on('Pick List', { }); }, add_get_items_button(frm) { - let source_doctype = frm.doc.items_based_on; - if (source_doctype != 'Sales Order') return; + let purpose = frm.doc.purpose; + if (purpose != 'Delivery against Sales Order' || 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 504a3fa14f..6eca1c67d4 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -6,7 +6,6 @@ "engine": "InnoDB", "field_order": [ "purpose", - "items_based_on", "customer", "work_order", "for_qty", @@ -48,16 +47,7 @@ "options": "Warehouse" }, { - "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\"", + "depends_on": "eval:doc.purpose==='Delivery against Sales Order'", "fieldname": "customer", "fieldtype": "Link", "in_list_view": 1, @@ -65,7 +55,7 @@ "options": "Customer" }, { - "depends_on": "eval:doc.items_based_on===\"Work Order\"", + "depends_on": "eval:doc.purpose==='Material Transfer for Manufacture'", "fieldname": "work_order", "fieldtype": "Link", "label": "Work Order", @@ -102,15 +92,15 @@ "read_only": 1 }, { - "default": "Material Transfer for manufacturing", + "default": "Material Transfer for Manufacture", "fieldname": "purpose", "fieldtype": "Select", "label": "Purpose", - "options": "Material Transfer for manufacturing\nMaterial Issue\nMaterial Transfer\nDelivery against Sales Order" + "options": "Material Transfer for Manufacture\nMaterial Issue\nMaterial Transfer\nDelivery against Sales Order" } ], "is_submittable": 1, - "modified": "2019-08-22 13:36:18.912659", + "modified": "2019-08-22 16:58:07.270447", "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 550c70f2d1..9b8bdfe6ee 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -7,6 +7,7 @@ import frappe import json from six import iteritems from frappe.model.document import Document +from frappe import _ from frappe.utils import floor, flt, today from frappe.model.mapper import get_mapped_doc, map_child_doc from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note as create_delivery_note_from_sales_order @@ -234,11 +235,14 @@ def set_delivery_note_missing_values(target): @frappe.whitelist() def create_stock_entry(pick_list): pick_list = frappe.get_doc(json.loads(pick_list)) + if stock_entry_exists(pick_list.get('name')): + return frappe.msgprint(_('Stock Entry already exists against this Pick List')) + work_order = frappe.get_doc("Work Order", pick_list.get('work_order')) stock_entry = frappe.new_doc('Stock Entry') stock_entry.pick_list = pick_list.get('name') - stock_entry.purpose = 'Material Transfer For Manufacture' + stock_entry.purpose = pick_list.get('purpose') stock_entry.set_stock_entry_type() stock_entry.work_order = work_order.name stock_entry.company = work_order.company @@ -304,4 +308,10 @@ def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filte }, as_dict=as_dict) def get_item_details(item_code): - pass \ No newline at end of file + pass + +@frappe.whitelist() +def stock_entry_exists(pick_list_name): + return frappe.db.exists('Stock Entry', { + 'pick_list': pick_list_name + }) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 22b84a18dd..f9e6d29104 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -618,7 +618,6 @@ { "fieldname": "pick_list", "fieldtype": "Link", - "hidden": 1, "label": "Pick List", "options": "Pick List", "read_only": 1 @@ -627,7 +626,7 @@ "icon": "fa fa-file-text", "idx": 1, "is_submittable": 1, - "modified": "2019-08-22 13:09:55.344036", + "modified": "2019-08-22 17:11:42.074154", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", From 59432418c6fe6dccfbc440f26c2c946ac0fbda59 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Fri, 23 Aug 2019 12:45:48 +0530 Subject: [PATCH 37/79] fix: Consider multiple stock entry against a purchase order --- erpnext/stock/doctype/stock_entry/stock_entry.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index e04d4c9512..2a7760edf0 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1165,8 +1165,11 @@ class StockEntry(StockController): and po.name = %s""", self.purchase_order)) #Update Supplied Qty in PO Supplied Items - frappe.db.sql("""UPDATE `tabPurchase Order Item Supplied` pos, `tabStock Entry Detail` sed - SET pos.supplied_qty = sed.transfer_qty where pos.name = sed.po_detail""") + + frappe.db.sql("""UPDATE `tabPurchase Order Item Supplied` pos + SET pos.supplied_qty = (SELECT ifnull(sum(transfer_qty), 0) FROM `tabStock Entry Detail` sed + WHERE pos.name = sed.po_detail and sed.docstatus = 1) + WHERE pos.docstatus = 1""") #Update reserved sub contracted quantity in bin based on Supplied Item Details and for d in self.get("items"): From 92095709374ddfdb68c155bcf0f1107fd8527454 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 26 Aug 2019 06:33:27 +0530 Subject: [PATCH 38/79] feat: Add option to create pick list from material request --- .../material_request/material_request.py | 21 ++++++++++++++ erpnext/stock/doctype/pick_list/pick_list.js | 28 +++++++++++-------- .../stock/doctype/pick_list/pick_list.json | 10 ++++++- erpnext/stock/doctype/pick_list/pick_list.py | 25 ++++++++++++++++- .../pick_list_item/pick_list_item.json | 17 +++++++++-- 5 files changed, 86 insertions(+), 15 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index f2fe44879d..a1cf2f269a 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -502,3 +502,24 @@ def raise_work_orders(material_request): frappe.throw(_("Productions Orders cannot be raised for:") + '\n' + new_line_sep(errors)) return work_orders + +@frappe.whitelist() +def create_pick_list(source_name, target_doc=None): + doc = get_mapped_doc('Material Request', source_name, { + 'Material Request': { + 'doctype': 'Pick List', + 'validation': { + 'docstatus': ['=', 1] + } + }, + 'Material Request Item': { + 'doctype': 'Pick List Reference Item', + 'field_map': { + 'name': 'material_request_item', + 'qty': 'stock_qty' + }, + # 'condition': lambda doc: abs(doc.transferred_qty) < abs(doc.required_qty) + }, + }, target_doc) + + return doc \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index c2dddab4c9..0f5ba57bc1 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -19,6 +19,13 @@ frappe.ui.form.on('Pick List', { } }; }); + frm.set_query('material_request', () => { + return { + filters: { + 'material_request_type': ['=', frm.doc.purpose] + } + }; + }); }, refresh: (frm) => { frm.trigger('add_get_items_button'); @@ -70,6 +77,15 @@ frappe.ui.form.on('Pick List', { }, __("Select Quantity"), __('Get Items')); }); }, + material_request: (frm) => { + frm.clear_table('items'); + frm.clear_table('locations'); + erpnext.utils.map_current_doc({ + method: 'erpnext.stock.doctype.material_request.material_request.create_pick_list', + target: frm, + source_name: frm.doc.material_request + }); + }, purpose: (frm) => { frm.clear_table('items'); frm.clear_table('locations'); @@ -116,14 +132,4 @@ frappe.ui.form.on('Pick List', { }); }); } -}); - - -// frappe.ui.form.on('Pick List Reference Item', { -// item_code: (frm, cdt, cdn) => { -// let row = locals[cdt][cdn]; -// if (row.item_code) { -// frappe.xcall(''); -// } -// } -// }); \ No newline at end of file +}); \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 6eca1c67d4..91fc6f8321 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -8,6 +8,7 @@ "purpose", "customer", "work_order", + "material_request", "for_qty", "column_break_4", "parent_warehouse", @@ -97,10 +98,17 @@ "fieldtype": "Select", "label": "Purpose", "options": "Material Transfer for Manufacture\nMaterial Issue\nMaterial Transfer\nDelivery against Sales Order" + }, + { + "depends_on": "eval:['Material Transfer', 'Material Issue'].includes(doc.purpose)", + "fieldname": "material_request", + "fieldtype": "Link", + "label": "Material Request", + "options": "Material Request" } ], "is_submittable": 1, - "modified": "2019-08-22 16:58:07.270447", + "modified": "2019-08-23 12:34:00.223445", "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 9b8bdfe6ee..a8955def74 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -41,6 +41,8 @@ class PickList(Document): 'item_code': item_code, 'sales_order': item_doc.sales_order, 'sales_order_item': item_doc.sales_order_item, + 'material_request': item_doc.material_request, + 'material_request_item': item_doc.material_request_item, 'uom': item_doc.uom, 'stock_uom': item_doc.stock_uom, 'conversion_factor': item_doc.conversion_factor, @@ -231,7 +233,6 @@ def set_delivery_note_missing_values(target): target.run_method('set_po_nos') target.run_method('calculate_taxes_and_totals') - @frappe.whitelist() def create_stock_entry(pick_list): pick_list = frappe.get_doc(json.loads(pick_list)) @@ -282,6 +283,28 @@ def create_stock_entry(pick_list): return stock_entry.as_dict() + +@frappe.whitelist() +def create_stock_entry_with_material_request_items(pick_list): + stock_entry = frappe.new_doc('Stock Entry') + stock_entry.pick_list = pick_list.get('name') + stock_entry.purpose = pick_list.get('purpose') + stock_entry.set_stock_entry_type() + + doc = get_mapped_doc("Work Order", source_name, { + "Work Order": { + "doctype": "Pick List", + "validation": { + "docstatus": ["=", 1] + } + }, + "Work Order Item": { + "doctype": "Pick List Reference Item", + "postprocess": update_item_quantity, + "condition": lambda doc: abs(doc.transferred_qty) < abs(doc.required_qty) + }, + }, target_doc) + @frappe.whitelist() def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filters, as_dict): return frappe.db.sql(""" 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 9ee806acf5..0a8f4cbebe 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -24,7 +24,9 @@ "batch_no", "column_break_15", "sales_order", - "sales_order_item" + "sales_order_item", + "material_request", + "material_request_item" ], "fields": [ { @@ -152,10 +154,21 @@ { "fieldname": "column_break_20", "fieldtype": "Column Break" + }, + { + "fieldname": "material_request", + "fieldtype": "Link", + "label": "Material Request", + "options": "Material Request" + }, + { + "fieldname": "material_request_item", + "fieldtype": "Data", + "label": "Material Request Item" } ], "istable": 1, - "modified": "2019-08-14 18:41:37.727388", + "modified": "2019-08-23 14:13:11.088354", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", From a1dc15269548767dfd40edde60aa5827117a192f Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 26 Aug 2019 06:57:43 +0530 Subject: [PATCH 39/79] fix: Move get items location button below items table --- erpnext/stock/doctype/pick_list/pick_list.js | 13 ++++++++----- erpnext/stock/doctype/pick_list/pick_list.json | 9 ++++++++- .../doctype/pick_list_item/pick_list_item.json | 8 +++++--- .../pick_list_reference_item.json | 8 +++++--- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 0f5ba57bc1..75d0511e50 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -27,14 +27,17 @@ frappe.ui.form.on('Pick List', { }; }); }, + get_item_locations(frm) { + frm.call('set_item_locations'); + }, refresh: (frm) => { frm.trigger('add_get_items_button'); - if (frm.doc.items && (frm.doc.items.length > 1 || frm.doc.items[0].item_code) && frm.doc.docstatus === 0) { - frm.add_custom_button(__('Get Item Locations'), () => { - frm.call('set_item_locations'); - }).addClass('btn-primary'); - } + // if (frm.doc.items && (frm.doc.items.length > 1 || frm.doc.items[0].item_code) && frm.doc.docstatus === 0) { + // frm.add_custom_button(__('Get Item Locations'), () => { + // frm.call('set_item_locations'); + // }).addClass('btn-primary'); + // } if (frm.doc.docstatus === 1) { if (frm.doc.purpose === 'Delivery against Sales Order') { frm.add_custom_button(__('Delivery Note'), () => frm.trigger('create_delivery_note'), __('Create')); diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 91fc6f8321..dbfd7d1bf2 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -15,6 +15,7 @@ "company", "section_break_4", "items", + "get_item_locations", "section_break_6", "locations", "amended_from" @@ -105,10 +106,16 @@ "fieldtype": "Link", "label": "Material Request", "options": "Material Request" + }, + { + "depends_on": "eval: doc.items.length && (doc.items.length > 1 || doc.items[0].item_code) && doc.docstatus === 0", + "fieldname": "get_item_locations", + "fieldtype": "Button", + "label": "Get Item Locations" } ], "is_submittable": 1, - "modified": "2019-08-23 12:34:00.223445", + "modified": "2019-08-26 06:52:04.532885", "modified_by": "Administrator", "module": "Stock", "name": "Pick List", 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 0a8f4cbebe..fdac9e58cf 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -159,16 +159,18 @@ "fieldname": "material_request", "fieldtype": "Link", "label": "Material Request", - "options": "Material Request" + "options": "Material Request", + "read_only": 1 }, { "fieldname": "material_request_item", "fieldtype": "Data", - "label": "Material Request Item" + "label": "Material Request Item", + "read_only": 1 } ], "istable": 1, - "modified": "2019-08-23 14:13:11.088354", + "modified": "2019-08-26 06:54:06.783255", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", diff --git a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json index e6984ef323..b549a08e10 100644 --- a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json +++ b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json @@ -85,16 +85,18 @@ "fieldname": "material_request", "fieldtype": "Link", "label": "Material Request", - "options": "Material Request" + "options": "Material Request", + "read_only": 1 }, { "fieldname": "material_request_item", "fieldtype": "Data", - "label": "Material Request Item" + "label": "Material Request Item", + "read_only": 1 } ], "istable": 1, - "modified": "2019-08-22 14:19:19.725540", + "modified": "2019-08-26 06:54:50.702552", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Reference Item", From be9f4ea48702b115c20c68de036ab3012c4d8830 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 26 Aug 2019 08:38:37 +0530 Subject: [PATCH 40/79] fix: Add button to create pick list from material request --- .../doctype/material_request/material_request.js | 15 ++++++++++++++- .../doctype/material_request/material_request.py | 3 +++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 96e31ff6ff..feaa4132b8 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -55,13 +55,19 @@ frappe.ui.form.on('Material Request', { if (frm.doc.docstatus == 1 && frm.doc.status != 'Stopped') { if (flt(frm.doc.per_ordered, 2) < 100) { - // make + let add_create_pick_list_button = () => { + frm.add_custom_button(__('Pick List'), + () => frm.events.create_pick_list(frm), __('Create')); + } + if (frm.doc.material_request_type === "Material Transfer") { + add_create_pick_list_button(); frm.add_custom_button(__("Transfer Material"), () => frm.events.make_stock_entry(frm), __('Create')); } if (frm.doc.material_request_type === "Material Issue") { + add_create_pick_list_button(); frm.add_custom_button(__("Issue Material"), () => frm.events.make_stock_entry(frm), __('Create')); } @@ -258,6 +264,13 @@ frappe.ui.form.on('Material Request', { }); }, + create_pick_list: (frm) => { + frappe.model.open_mapped_doc({ + method: "erpnext.stock.doctype.material_request.material_request.create_pick_list", + frm: frm + }); + }, + raise_work_orders: function(frm) { frappe.call({ method:"erpnext.stock.doctype.material_request.material_request.raise_work_orders", diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index a1cf2f269a..7040d83215 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -508,6 +508,9 @@ def create_pick_list(source_name, target_doc=None): doc = get_mapped_doc('Material Request', source_name, { 'Material Request': { 'doctype': 'Pick List', + 'field_map': { + 'material_request_type': 'purpose' + }, 'validation': { 'docstatus': ['=', 1] } From ecc801d3d835e117c09345e6c0a7ff63ea2f4f63 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 26 Aug 2019 08:39:11 +0530 Subject: [PATCH 41/79] fix: Add pick list field to delivery note --- .../doctype/delivery_note/delivery_note.json | 5480 ++++------------- 1 file changed, 1137 insertions(+), 4343 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index b5f2c3404e..1116273ace 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -1,4514 +1,1308 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2013-05-24 19:29:09", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-05-24 19:29:09", + "doctype": "DocType", + "document_type": "Document", + "field_order": [ + "delivery_to_section", + "column_break0", + "title", + "naming_series", + "customer", + "customer_name", + "column_break1", + "amended_from", + "company", + "posting_date", + "posting_time", + "set_posting_time", + "is_return", + "issue_credit_note", + "return_against", + "customer_po_details", + "po_no", + "section_break_18", + "pick_list", + "column_break_17", + "po_date", + "contact_info", + "shipping_address_name", + "shipping_address", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "col_break21", + "customer_address", + "tax_id", + "address_display", + "company_address", + "company_address_display", + "currency_and_price_list", + "currency", + "conversion_rate", + "col_break23", + "selling_price_list", + "price_list_currency", + "plc_conversion_rate", + "ignore_pricing_rule", + "sec_warehouse", + "set_warehouse", + "col_break_warehouse", + "to_warehouse", + "items_section", + "scan_barcode", + "items", + "pricing_rule_details", + "pricing_rules", + "packing_list", + "packed_items", + "product_bundle_help", + "section_break_31", + "total_qty", + "base_total", + "base_net_total", + "column_break_33", + "total", + "net_total", + "total_net_weight", + "taxes_section", + "tax_category", + "column_break_39", + "shipping_rule", + "section_break_41", + "taxes_and_charges", + "taxes", + "sec_tax_breakup", + "other_charges_calculation", + "section_break_44", + "base_total_taxes_and_charges", + "column_break_47", + "total_taxes_and_charges", + "section_break_49", + "apply_discount_on", + "base_discount_amount", + "column_break_51", + "additional_discount_percentage", + "discount_amount", + "totals", + "base_grand_total", + "base_rounding_adjustment", + "base_rounded_total", + "base_in_words", + "column_break3", + "grand_total", + "rounding_adjustment", + "rounded_total", + "in_words", + "terms_section_break", + "tc_name", + "terms", + "transporter_info", + "transporter", + "driver", + "lr_no", + "vehicle_no", + "col_break34", + "transporter_name", + "driver_name", + "lr_date", + "more_info", + "project", + "campaign", + "source", + "column_break5", + "per_billed", + "customer_group", + "territory", + "printing_details", + "letter_head", + "select_print_heading", + "language", + "column_break_88", + "print_without_amount", + "group_same_items", + "section_break_83", + "status", + "per_installed", + "installation_status", + "column_break_89", + "excise_page", + "instructions", + "subscription_section", + "auto_repeat", + "sales_team_section_break", + "sales_partner", + "column_break7", + "commission_rate", + "total_commission", + "section_break1", + "sales_team" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "delivery_to_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Delivery To", - "length": 0, - "no_copy": 0, - "options": "fa fa-user", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "delivery_to_section", + "fieldtype": "Section Break", + "label": "Delivery To", + "options": "fa fa-user" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break0", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break0", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "print_width": "50%", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "{customer_name}", - "fieldname": "title", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "{customer_name}", + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1, + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 1, - "oldfieldname": "naming_series", - "oldfieldtype": "Select", - "options": "MAT-DN-.YYYY.-", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "oldfieldname": "naming_series", + "oldfieldtype": "Select", + "options": "MAT-DN-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "customer", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Customer", - "length": 0, - "no_copy": 0, - "oldfieldname": "customer", - "oldfieldtype": "Link", - "options": "Customer", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "customer", + "fieldtype": "Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Customer", + "oldfieldname": "customer", + "oldfieldtype": "Link", + "options": "Customer", + "print_hide": 1, + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "depends_on": "customer", - "fetch_from": "customer.customer_name", - "fieldname": "customer_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "depends_on": "customer", + "fetch_from": "customer.customer_name", + "fieldname": "customer_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Customer Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Delivery Note", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Delivery Note", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "print_hide": 1, + "print_width": "150px", + "remember_last_selected_value": 1, + "reqd": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "depends_on": "", - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date", - "length": 0, - "no_copy": 1, - "oldfieldname": "posting_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Date", + "no_copy": 1, + "oldfieldname": "posting_date", + "oldfieldtype": "Date", + "print_width": "100px", + "reqd": 1, + "search_index": 1, "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "", - "fieldname": "posting_time", - "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Posting Time", - "length": 0, - "no_copy": 0, - "oldfieldname": "posting_time", - "oldfieldtype": "Time", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "posting_time", + "fieldtype": "Time", + "label": "Posting Time", + "oldfieldname": "posting_time", + "oldfieldtype": "Time", + "print_hide": 1, + "print_width": "100px", + "reqd": 1, "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.docstatus==0", - "fieldname": "set_posting_time", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Edit Posting Date and Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:doc.docstatus==0", + "fieldname": "set_posting_time", + "fieldtype": "Check", + "label": "Edit Posting Date and Time", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_return", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Return", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "is_return", + "fieldtype": "Check", + "label": "Is Return", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_return", - "fieldname": "issue_credit_note", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Issue Credit Note", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "is_return", + "fieldname": "issue_credit_note", + "fieldtype": "Check", + "label": "Issue Credit Note" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_return", - "fieldname": "return_against", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Return Against Delivery Note", - "length": 0, - "no_copy": 1, - "options": "Delivery Note", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "is_return", + "fieldname": "return_against", + "fieldtype": "Link", + "label": "Return Against Delivery Note", + "no_copy": 1, + "options": "Delivery Note", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "po_no", - "columns": 0, - "fieldname": "customer_po_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer PO Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "po_no", + "fieldname": "customer_po_details", + "fieldtype": "Section Break", + "label": "Customer PO Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "po_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer's Purchase Order No", - "length": 0, - "no_copy": 1, - "oldfieldname": "po_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "allow_on_submit": 1, + "fieldname": "po_no", + "fieldtype": "Data", + "label": "Customer's Purchase Order No", + "no_copy": 1, + "oldfieldname": "po_no", + "oldfieldtype": "Data", + "print_hide": 1, + "print_width": "100px", "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_17", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.po_no", - "fieldname": "po_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer's Purchase Order Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "po_date", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "depends_on": "eval:doc.po_no", + "fieldname": "po_date", + "fieldtype": "Date", + "label": "Customer's Purchase Order Date", + "oldfieldname": "po_date", + "oldfieldtype": "Data", + "print_hide": 1, + "print_width": "100px", + "read_only": 1, "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "depends_on": "customer", - "fieldname": "contact_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Address and Contact", - "length": 0, - "no_copy": 0, - "options": "fa fa-bullhorn", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "depends_on": "customer", + "fieldname": "contact_info", + "fieldtype": "Section Break", + "label": "Address and Contact", + "options": "fa fa-bullhorn" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "shipping_address_name", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Shipping Address", - "length": 0, - "no_copy": 0, - "options": "Address", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "shipping_address_name", + "fieldtype": "Link", + "label": "Shipping Address", + "options": "Address", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "shipping_address", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Shipping Address", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "shipping_address", + "fieldtype": "Small Text", + "label": "Shipping Address", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_person", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contact Person", - "length": 0, - "no_copy": 0, - "options": "Contact", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_person", + "fieldtype": "Link", + "label": "Contact Person", + "options": "Contact", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_display", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contact", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_display", + "fieldtype": "Small Text", + "in_global_search": 1, + "label": "Contact", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_mobile", - "fieldtype": "Small Text", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Mobile No", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_mobile", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Mobile No", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_email", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contact Email", - "length": 0, - "no_copy": 0, - "options": "Email", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_email", + "fieldtype": "Data", + "hidden": 1, + "label": "Contact Email", + "options": "Email", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break21", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "col_break21", + "fieldtype": "Column Break", + "print_width": "50%", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "customer", - "fieldname": "customer_address", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Billing Address Name", - "length": 0, - "no_copy": 0, - "options": "Address", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "customer", + "fieldname": "customer_address", + "fieldtype": "Link", + "label": "Billing Address Name", + "options": "Address", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tax_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax Id", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "tax_id", + "fieldtype": "Data", + "label": "Tax Id", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_display", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Billing Address", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company_address", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company Address Name", - "length": 0, - "no_copy": 0, - "options": "Address", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company_address", + "fieldtype": "Link", + "label": "Company Address Name", + "options": "Address" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company_address_display", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company Address", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company_address_display", + "fieldtype": "Small Text", + "label": "Company Address" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "currency_and_price_list", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Currency and Price List", - "length": 0, - "no_copy": 0, - "options": "fa fa-tag", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "currency_and_price_list", + "fieldtype": "Section Break", + "label": "Currency and Price List", + "options": "fa fa-tag" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "currency", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Currency", - "length": 0, - "no_copy": 0, - "oldfieldname": "currency", - "oldfieldtype": "Select", - "options": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "oldfieldname": "currency", + "oldfieldtype": "Select", + "options": "Currency", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Rate at which customer's currency is converted to company's base currency", - "fieldname": "conversion_rate", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Exchange Rate", - "length": 0, - "no_copy": 0, - "oldfieldname": "conversion_rate", - "oldfieldtype": "Currency", - "permlevel": 0, - "precision": "9", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Rate at which customer's currency is converted to company's base currency", + "fieldname": "conversion_rate", + "fieldtype": "Float", + "label": "Exchange Rate", + "oldfieldname": "conversion_rate", + "oldfieldtype": "Currency", + "precision": "9", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break23", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "col_break23", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "selling_price_list", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List", - "length": 0, - "no_copy": 0, - "oldfieldname": "price_list_name", - "oldfieldtype": "Select", - "options": "Price List", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "selling_price_list", + "fieldtype": "Link", + "label": "Price List", + "oldfieldname": "price_list_name", + "oldfieldtype": "Select", + "options": "Price List", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "price_list_currency", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "price_list_currency", + "fieldtype": "Link", + "label": "Price List Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Rate at which Price list currency is converted to company's base currency", - "fieldname": "plc_conversion_rate", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List Exchange Rate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "9", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Rate at which Price list currency is converted to company's base currency", + "fieldname": "plc_conversion_rate", + "fieldtype": "Float", + "label": "Price List Exchange Rate", + "precision": "9", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ignore_pricing_rule", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Ignore Pricing Rule", - "length": 0, - "no_copy": 1, - "permlevel": 1, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "ignore_pricing_rule", + "fieldtype": "Check", + "label": "Ignore Pricing Rule", + "no_copy": 1, + "permlevel": 1, + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sec_warehouse", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sec_warehouse", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "set_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Set Source Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "set_warehouse", + "fieldtype": "Link", + "label": "Set Source Warehouse", + "options": "Warehouse", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break_warehouse", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "col_break_warehouse", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Required only for sample item.", - "fieldname": "to_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "To Warehouse", - "length": 0, - "no_copy": 1, - "oldfieldname": "to_warehouse", - "oldfieldtype": "Link", - "options": "Warehouse", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Required only for sample item.", + "fieldname": "to_warehouse", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "To Warehouse", + "no_copy": 1, + "oldfieldname": "to_warehouse", + "oldfieldtype": "Link", + "options": "Warehouse", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "items_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "items_section", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "fa fa-shopping-cart" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "scan_barcode", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Scan Barcode", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "scan_barcode", + "fieldtype": "Data", + "label": "Scan Barcode" + }, { - "allow_bulk_edit": 1, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Items", - "length": 0, - "no_copy": 0, - "oldfieldname": "delivery_note_details", - "oldfieldtype": "Table", - "options": "Delivery Note Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_bulk_edit": 1, + "fieldname": "items", + "fieldtype": "Table", + "label": "Items", + "oldfieldname": "delivery_note_details", + "oldfieldtype": "Table", + "options": "Delivery Note Item", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "fieldname": "pricing_rule_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Pricing Rules", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "pricing_rule_details", + "fieldtype": "Section Break", + "label": "Pricing Rules" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "fieldname": "pricing_rules", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Pricing Rule Detail", - "length": 0, - "no_copy": 0, - "options": "Pricing Rule Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "pricing_rules", + "fieldtype": "Table", + "label": "Pricing Rule Detail", + "options": "Pricing Rule Detail", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "packed_items", - "columns": 0, - "fieldname": "packing_list", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Packing List", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-suitcase", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "packed_items", + "fieldname": "packing_list", + "fieldtype": "Section Break", + "label": "Packing List", + "oldfieldtype": "Section Break", + "options": "fa fa-suitcase", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "packed_items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Packed Items", - "length": 0, - "no_copy": 0, - "oldfieldname": "packing_details", - "oldfieldtype": "Table", - "options": "Packed Item", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "packed_items", + "fieldtype": "Table", + "label": "Packed Items", + "oldfieldname": "packing_details", + "oldfieldtype": "Table", + "options": "Packed Item", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "product_bundle_help", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Product Bundle Help", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "product_bundle_help", + "fieldtype": "HTML", + "label": "Product Bundle Help", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_31", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_31", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_qty", + "fieldtype": "Float", + "label": "Total Quantity", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total (Company Currency)", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "base_total", + "fieldtype": "Currency", + "label": "Total (Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_net_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Net Total (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "net_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "base_net_total", + "fieldtype": "Currency", + "label": "Net Total (Company Currency)", + "oldfieldname": "net_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "column_break_33", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_33", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total", + "fieldtype": "Currency", + "label": "Total", + "options": "currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "net_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Net Total", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "net_total", + "fieldtype": "Currency", + "label": "Net Total", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_net_weight", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Net Weight", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_net_weight", + "fieldtype": "Float", + "label": "Total Net Weight", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "taxes_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Taxes and Charges", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-money", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "taxes_section", + "fieldtype": "Section Break", + "label": "Taxes and Charges", + "oldfieldtype": "Section Break", + "options": "fa fa-money" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tax_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax Category", - "length": 0, - "no_copy": 0, - "options": "Tax Category", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "tax_category", + "fieldtype": "Link", + "label": "Tax Category", + "options": "Tax Category", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_39", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_39", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "shipping_rule", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Shipping Rule", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Button", - "options": "Shipping Rule", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "shipping_rule", + "fieldtype": "Link", + "label": "Shipping Rule", + "oldfieldtype": "Button", + "options": "Shipping Rule", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_41", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_41", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.", - "fieldname": "taxes_and_charges", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Taxes and Charges Template", - "length": 0, - "no_copy": 0, - "oldfieldname": "charge", - "oldfieldtype": "Link", - "options": "Sales Taxes and Charges Template", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.", + "fieldname": "taxes_and_charges", + "fieldtype": "Link", + "label": "Sales Taxes and Charges Template", + "oldfieldname": "charge", + "oldfieldtype": "Link", + "options": "Sales Taxes and Charges Template", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "taxes", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Taxes and Charges", - "length": 0, - "no_copy": 0, - "oldfieldname": "other_charges", - "oldfieldtype": "Table", - "options": "Sales Taxes and Charges", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Sales Taxes and Charges", + "oldfieldname": "other_charges", + "oldfieldtype": "Table", + "options": "Sales Taxes and Charges" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "sec_tax_breakup", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax Breakup", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "sec_tax_breakup", + "fieldtype": "Section Break", + "label": "Tax Breakup" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "other_charges_calculation", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Taxes and Charges Calculation", - "length": 0, - "no_copy": 1, - "oldfieldtype": "HTML", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "other_charges_calculation", + "fieldtype": "Text", + "label": "Taxes and Charges Calculation", + "no_copy": 1, + "oldfieldtype": "HTML", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_44", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_44", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_total_taxes_and_charges", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Taxes and Charges (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "other_charges_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "base_total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges (Company Currency)", + "oldfieldname": "other_charges_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_47", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_47", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_taxes_and_charges", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Taxes and Charges", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "discount_amount", - "columns": 0, - "fieldname": "section_break_49", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Discount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "discount_amount", + "fieldname": "section_break_49", + "fieldtype": "Section Break", + "label": "Additional Discount" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Grand Total", - "fieldname": "apply_discount_on", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Apply Additional Discount On", - "length": 0, - "no_copy": 0, - "options": "\nGrand Total\nNet Total", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Grand Total", + "fieldname": "apply_discount_on", + "fieldtype": "Select", + "label": "Apply Additional Discount On", + "options": "\nGrand Total\nNet Total", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_discount_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Discount Amount (Company Currency)", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "base_discount_amount", + "fieldtype": "Currency", + "label": "Additional Discount Amount (Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_51", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_51", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "additional_discount_percentage", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Discount Percentage", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "additional_discount_percentage", + "fieldtype": "Float", + "label": "Additional Discount Percentage", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discount_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Discount Amount", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discount_amount", + "fieldtype": "Currency", + "label": "Additional Discount Amount", + "options": "currency", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "totals", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-money", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "totals", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "fa fa-money" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_grand_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Grand Total (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "grand_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "base_grand_total", + "fieldtype": "Currency", + "label": "Grand Total (Company Currency)", + "oldfieldname": "grand_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_rounding_adjustment", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounding Adjustment (Company Currency)", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "base_rounding_adjustment", + "fieldtype": "Currency", + "label": "Rounding Adjustment (Company Currency)", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_rounded_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounded Total (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "rounded_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "base_rounded_total", + "fieldtype": "Currency", + "label": "Rounded Total (Company Currency)", + "oldfieldname": "rounded_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "In Words will be visible once you save the Delivery Note.", - "fieldname": "base_in_words", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In Words (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "in_words", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "200px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "description": "In Words will be visible once you save the Delivery Note.", + "fieldname": "base_in_words", + "fieldtype": "Data", + "label": "In Words (Company Currency)", + "oldfieldname": "in_words", + "oldfieldtype": "Data", + "print_hide": 1, + "print_width": "200px", + "read_only": 1, "width": "200px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break3", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "grand_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Grand Total", - "length": 0, - "no_copy": 0, - "oldfieldname": "grand_total_export", - "oldfieldtype": "Currency", - "options": "currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "grand_total", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Grand Total", + "oldfieldname": "grand_total_export", + "oldfieldtype": "Currency", + "options": "currency", + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rounding_adjustment", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounding Adjustment", - "length": 0, - "no_copy": 1, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "rounding_adjustment", + "fieldtype": "Currency", + "label": "Rounding Adjustment", + "no_copy": 1, + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fieldname": "rounded_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounded Total", - "length": 0, - "no_copy": 0, - "oldfieldname": "rounded_total_export", - "oldfieldtype": "Currency", - "options": "currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "bold": 1, + "fieldname": "rounded_total", + "fieldtype": "Currency", + "label": "Rounded Total", + "oldfieldname": "rounded_total_export", + "oldfieldtype": "Currency", + "options": "currency", + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "In Words (Export) will be visible once you save the Delivery Note.", - "fieldname": "in_words", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In Words", - "length": 0, - "no_copy": 0, - "oldfieldname": "in_words_export", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "description": "In Words (Export) will be visible once you save the Delivery Note.", + "fieldname": "in_words", + "fieldtype": "Data", + "label": "In Words", + "oldfieldname": "in_words_export", + "oldfieldtype": "Data", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "terms", - "columns": 0, - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Terms and Conditions", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-legal", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "terms", + "fieldname": "terms_section_break", + "fieldtype": "Section Break", + "label": "Terms and Conditions", + "oldfieldtype": "Section Break", + "options": "fa fa-legal" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tc_name", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Terms", - "length": 0, - "no_copy": 0, - "oldfieldname": "tc_name", - "oldfieldtype": "Link", - "options": "Terms and Conditions", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "tc_name", + "fieldtype": "Link", + "label": "Terms", + "oldfieldname": "tc_name", + "oldfieldtype": "Link", + "options": "Terms and Conditions", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "terms", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Terms and Conditions Details", - "length": 0, - "no_copy": 0, - "oldfieldname": "terms", - "oldfieldtype": "Text Editor", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "terms", + "fieldtype": "Text Editor", + "label": "Terms and Conditions Details", + "oldfieldname": "terms", + "oldfieldtype": "Text Editor" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "transporter", - "columns": 0, - "fieldname": "transporter_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transporter Info", - "length": 0, - "no_copy": 0, - "options": "fa fa-truck", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "transporter", + "fieldname": "transporter_info", + "fieldtype": "Section Break", + "label": "Transporter Info", + "options": "fa fa-truck", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transporter", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transporter", - "length": 0, - "no_copy": 0, - "options": "Supplier", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "transporter", + "fieldtype": "Link", + "label": "Transporter", + "options": "Supplier", + "print_hide": 1, + "print_width": "150px", "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "driver", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Driver", - "length": 0, - "no_copy": 0, - "options": "Driver", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "driver", + "fieldtype": "Link", + "label": "Driver", + "options": "Driver", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "lr_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transport Receipt No", - "length": 0, - "no_copy": 0, - "oldfieldname": "lr_no", - "oldfieldtype": "Data", - "options": "", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "lr_no", + "fieldtype": "Data", + "label": "Transport Receipt No", + "oldfieldname": "lr_no", + "oldfieldtype": "Data", + "print_hide": 1, + "print_width": "100px", "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "vehicle_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Vehicle No", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "vehicle_no", + "fieldtype": "Data", + "label": "Vehicle No", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break34", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "col_break34", + "fieldtype": "Column Break", + "print_width": "50%", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "transporter.name", - "fieldname": "transporter_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transporter Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "transporter.name", + "fieldname": "transporter_name", + "fieldtype": "Data", + "label": "Transporter Name", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "driver.full_name", - "fieldname": "driver_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Driver Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "driver.full_name", + "fieldname": "driver_name", + "fieldtype": "Data", + "label": "Driver Name", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "description": "", - "fieldname": "lr_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transport Receipt Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "lr_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "default": "Today", + "fieldname": "lr_date", + "fieldtype": "Date", + "label": "Transport Receipt Date", + "oldfieldname": "lr_date", + "oldfieldtype": "Date", + "print_hide": 1, + "print_width": "100px", "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "more_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "more_info", + "fieldtype": "Section Break", + "label": "More Information", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Track this Delivery Note against any Project", - "fieldname": "project", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Project", - "length": 0, - "no_copy": 0, - "oldfieldname": "project", - "oldfieldtype": "Link", - "options": "Project", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Track this Delivery Note against any Project", + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "oldfieldname": "project", + "oldfieldtype": "Link", + "options": "Project" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "campaign", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Campaign", - "length": 0, - "no_copy": 0, - "oldfieldname": "campaign", - "oldfieldtype": "Link", - "options": "Campaign", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "campaign", + "fieldtype": "Link", + "label": "Campaign", + "oldfieldname": "campaign", + "oldfieldtype": "Link", + "options": "Campaign", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "source", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Source", - "length": 0, - "no_copy": 0, - "oldfieldname": "source", - "oldfieldtype": "Select", - "options": "Lead Source", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "source", + "fieldtype": "Link", + "label": "Source", + "oldfieldname": "source", + "oldfieldtype": "Select", + "options": "Lead Source", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break5", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "print_hide": 1, + "print_width": "50%", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "per_billed", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "% Amount Billed", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "per_billed", + "fieldtype": "Percent", + "label": "% Amount Billed", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "customer_group", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer Group", - "length": 0, - "no_copy": 0, - "options": "Customer Group", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "customer_group", + "fieldtype": "Link", + "hidden": 1, + "label": "Customer Group", + "options": "Customer Group", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "territory", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Territory", - "length": 0, - "no_copy": 0, - "options": "Territory", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "territory", + "fieldtype": "Link", + "label": "Territory", + "options": "Territory", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "printing_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Printing Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "printing_details", + "fieldtype": "Section Break", + "label": "Printing Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "letter_head", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Letter Head", - "length": 0, - "no_copy": 0, - "oldfieldname": "letter_head", - "oldfieldtype": "Link", - "options": "Letter Head", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "oldfieldname": "letter_head", + "oldfieldtype": "Link", + "options": "Letter Head", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_print_heading", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Heading", - "length": 0, - "no_copy": 1, - "oldfieldname": "select_print_heading", - "oldfieldtype": "Link", - "options": "Print Heading", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "select_print_heading", + "fieldtype": "Link", + "label": "Print Heading", + "no_copy": 1, + "oldfieldname": "select_print_heading", + "oldfieldtype": "Link", + "options": "Print Heading", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "language", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Language", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "language", + "fieldtype": "Data", + "label": "Print Language", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_88", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_88", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "print_without_amount", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Without Amount", - "length": 0, - "no_copy": 0, - "oldfieldname": "print_without_amount", - "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "0", + "fieldname": "print_without_amount", + "fieldtype": "Check", + "label": "Print Without Amount", + "oldfieldname": "print_without_amount", + "oldfieldtype": "Check", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "group_same_items", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Group same items", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "0", + "fieldname": "group_same_items", + "fieldtype": "Check", + "label": "Group same items", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "section_break_83", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "section_break_83", + "fieldtype": "Section Break", + "label": "Status" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Draft", - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Status", - "length": 0, - "no_copy": 1, - "oldfieldname": "status", - "oldfieldtype": "Select", - "options": "\nDraft\nTo Bill\nCompleted\nCancelled\nClosed", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "oldfieldname": "status", + "oldfieldtype": "Select", + "options": "\nDraft\nTo Bill\nCompleted\nCancelled\nClosed", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, + "reqd": 1, + "search_index": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "description": "% of materials delivered against this Delivery Note", - "fieldname": "per_installed", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "% Installed", - "length": 0, - "no_copy": 1, - "oldfieldname": "per_installed", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:!doc.__islocal", + "description": "% of materials delivered against this Delivery Note", + "fieldname": "per_installed", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "% Installed", + "no_copy": 1, + "oldfieldname": "per_installed", + "oldfieldtype": "Currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "installation_status", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Installation Status", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "installation_status", + "fieldtype": "Select", + "hidden": 1, + "label": "Installation Status", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_89", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_89", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "excise_page", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Excise Page Number", - "length": 0, - "no_copy": 0, - "oldfieldname": "excise_page", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "excise_page", + "fieldtype": "Data", + "hidden": 1, + "label": "Excise Page Number", + "oldfieldname": "excise_page", + "oldfieldtype": "Data", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "instructions", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Instructions", - "length": 0, - "no_copy": 0, - "oldfieldname": "instructions", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "instructions", + "fieldtype": "Text", + "label": "Instructions", + "oldfieldname": "instructions", + "oldfieldtype": "Text" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subscription_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subscription Section", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "subscription_section", + "fieldtype": "Section Break", + "label": "Subscription Section" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "auto_repeat", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Auto Repeat", - "length": 0, - "no_copy": 1, - "options": "Auto Repeat", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "auto_repeat", + "fieldtype": "Link", + "label": "Auto Repeat", + "no_copy": 1, + "options": "Auto Repeat", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "total_commission", - "columns": 0, - "fieldname": "sales_team_section_break", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Commission", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-group", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "total_commission", + "fieldname": "sales_team_section_break", + "fieldtype": "Section Break", + "label": "Commission", + "oldfieldtype": "Section Break", + "options": "fa fa-group", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_partner", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Partner", - "length": 0, - "no_copy": 0, - "oldfieldname": "sales_partner", - "oldfieldtype": "Link", - "options": "Sales Partner", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "sales_partner", + "fieldtype": "Link", + "label": "Sales Partner", + "oldfieldname": "sales_partner", + "oldfieldtype": "Link", + "options": "Sales Partner", + "print_hide": 1, + "print_width": "150px", "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break7", + "fieldtype": "Column Break", + "print_hide": 1, + "print_width": "50%", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "commission_rate", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Commission Rate (%)", - "length": 0, - "no_copy": 0, - "oldfieldname": "commission_rate", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "commission_rate", + "fieldtype": "Float", + "label": "Commission Rate (%)", + "oldfieldname": "commission_rate", + "oldfieldtype": "Currency", + "print_hide": 1, + "print_width": "100px", "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_commission", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Commission", - "length": 0, - "no_copy": 0, - "oldfieldname": "total_commission", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_commission", + "fieldtype": "Currency", + "label": "Total Commission", + "oldfieldname": "total_commission", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "sales_team", - "columns": 0, - "fieldname": "section_break1", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Team", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "sales_team", + "fieldname": "section_break1", + "fieldtype": "Section Break", + "label": "Sales Team", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_team", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Team1", - "length": 0, - "no_copy": 0, - "oldfieldname": "sales_team", - "oldfieldtype": "Table", - "options": "Sales Team", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "allow_on_submit": 1, + "fieldname": "sales_team", + "fieldtype": "Table", + "label": "Sales Team", + "oldfieldname": "sales_team", + "oldfieldtype": "Table", + "options": "Sales Team", + "print_hide": 1 + }, + { + "fieldname": "pick_list", + "fieldtype": "Link", + "hidden": 1, + "label": "Pick List", + "options": "Pick List", + "read_only": 1 + }, + { + "fieldname": "section_break_18", + "fieldtype": "Section Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-truck", - "idx": 146, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2019-02-13 01:06:29.783590", - "modified_by": "Administrator", - "module": "Stock", - "name": "Delivery Note", - "owner": "Administrator", + ], + "icon": "fa fa-truck", + "idx": 146, + "is_submittable": 1, + "modified": "2019-08-26 07:37:39.766014", + "modified_by": "Administrator", + "module": "Stock", + "name": "Delivery Note", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "read": 1, + "report": 1, + "role": "Accounts User" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 1, - "print": 0, - "read": 1, - "report": 0, - "role": "Stock Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "permlevel": 1, + "read": 1, + "role": "Stock Manager", "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 1, - "search_fields": "status,customer,customer_name, territory,base_grand_total", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "customer", - "title_field": "title", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 -} + ], + "search_fields": "status,customer,customer_name, territory,base_grand_total", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "timeline_field": "customer", + "title_field": "title", + "track_changes": 1, + "track_seen": 1 +} \ No newline at end of file From 94d7096c8ccd1c7a1fbde5491b22f093cb6982a4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 26 Aug 2019 08:40:02 +0530 Subject: [PATCH 42/79] fix: Show create button only if target doc does not exists --- erpnext/stock/doctype/pick_list/pick_list.js | 25 +-- erpnext/stock/doctype/pick_list/pick_list.py | 185 +++++++++++-------- 2 files changed, 120 insertions(+), 90 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 75d0511e50..a0082b859c 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -32,23 +32,18 @@ frappe.ui.form.on('Pick List', { }, refresh: (frm) => { frm.trigger('add_get_items_button'); - - // if (frm.doc.items && (frm.doc.items.length > 1 || frm.doc.items[0].item_code) && frm.doc.docstatus === 0) { - // frm.add_custom_button(__('Get Item Locations'), () => { - // frm.call('set_item_locations'); - // }).addClass('btn-primary'); - // } if (frm.doc.docstatus === 1) { - if (frm.doc.purpose === 'Delivery against Sales Order') { - frm.add_custom_button(__('Delivery Note'), () => frm.trigger('create_delivery_note'), __('Create')); - } else { - frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.stock_entry_exists', { - 'pick_list_name': frm.doc.name - }).then(exists => { - if (exists) return; + frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.target_document_exists', { + 'pick_list_name': frm.doc.name, + 'purpose': frm.doc.purpose + }).then(target_document_exists => { + if (target_document_exists) return; + if (frm.doc.purpose === 'Delivery against Sales Order') { + 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')); - }); - } + } + }); } }, work_order: (frm) => { diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index a8955def74..16bb9e6dc7 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -170,6 +170,7 @@ def get_item_locations_based_on_batch_nos(item_doc): return locations + @frappe.whitelist() def create_delivery_note(source_name, target_doc=None): pick_list = frappe.get_doc('Pick List', source_name) @@ -181,18 +182,18 @@ def create_delivery_note(source_name, target_doc=None): delivery_note = create_delivery_note_from_sales_order(sales_order, delivery_note, skip_item_mapping=True) + item_table_mapper = { + 'doctype': 'Delivery Note Item', + 'field_map': { + 'rate': 'rate', + 'name': 'so_detail', + 'parent': 'against_sales_order', + }, + 'condition': lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 + } + 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', - 'field_map': { - 'rate': 'rate', - 'name': 'so_detail', - 'parent': 'against_sales_order', - }, - 'condition': lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 - } - dn_item = map_child_doc(sales_order_item, delivery_note, item_table_mapper) if dn_item: @@ -203,79 +204,26 @@ def create_delivery_note(source_name, target_doc=None): set_delivery_note_missing_values(delivery_note) + delivery_note.pick_list = pick_list.name + return delivery_note - -def update_delivery_note_item(source, target, delivery_note): - cost_center = frappe.db.get_value("Project", delivery_note.project, "cost_center") - if not cost_center: - cost_center = frappe.db.get_value('Item Default', - fieldname=['buying_cost_center'], - filters={ - 'parent': source.item_code, - 'parenttype': 'Item', - 'company': delivery_note.company - }) - - if not cost_center: - cost_center = frappe.db.get_value('Item Default', - fieldname=['buying_cost_center'], - filters={ - 'parent': source.item_group, - 'parenttype': 'Item Group', - 'company': delivery_note.company - }) - - target.cost_center = cost_center - -def set_delivery_note_missing_values(target): - target.run_method('set_missing_values') - target.run_method('set_po_nos') - target.run_method('calculate_taxes_and_totals') - @frappe.whitelist() def create_stock_entry(pick_list): pick_list = frappe.get_doc(json.loads(pick_list)) - if stock_entry_exists(pick_list.get('name')): - return frappe.msgprint(_('Stock Entry already exists against this Pick List')) - work_order = frappe.get_doc("Work Order", pick_list.get('work_order')) + if stock_entry_exists(pick_list.get('name')): + return frappe.msgprint(_('Stock Entry has been already created against this Pick List')) stock_entry = frappe.new_doc('Stock Entry') stock_entry.pick_list = pick_list.get('name') stock_entry.purpose = pick_list.get('purpose') stock_entry.set_stock_entry_type() - stock_entry.work_order = work_order.name - stock_entry.company = work_order.company - stock_entry.from_bom = 1 - stock_entry.bom_no = work_order.bom_no - stock_entry.use_multi_level_bom = work_order.use_multi_level_bom - stock_entry.fg_completed_qty = pick_list.for_qty - if work_order.bom_no: - stock_entry.inspection_required = frappe.db.get_value('BOM', - work_order.bom_no, 'inspection_required') - is_wip_warehouse_group = frappe.db.get_value('Warehouse', work_order.wip_warehouse, 'is_group') - if not (is_wip_warehouse_group and work_order.skip_transfer): - wip_warehouse = work_order.wip_warehouse - else: - wip_warehouse = None - stock_entry.to_warehouse = wip_warehouse - - stock_entry.project = work_order.project - - for location in pick_list.locations: - item = frappe._dict() - item.item_code = location.item_code - item.s_warehouse = location.warehouse - item.t_warehouse = wip_warehouse - item.qty = location.qty - item.transfer_qty = location.stock_qty - item.uom = location.uom - item.conversion_factor = location.conversion_factor - item.stock_uom = location.stock_uom - - stock_entry.append('items', item) + if pick_list.get('work_order'): + stock_entry = update_stock_entry_based_on_work_order(pick_list, stock_entry) + elif pick_list.get('material_request'): + stock_entry = update_stock_entry_based_on_material_request(pick_list, stock_entry) stock_entry.set_incoming_rate() stock_entry.set_actual_qty() @@ -283,7 +231,6 @@ def create_stock_entry(pick_list): return stock_entry.as_dict() - @frappe.whitelist() def create_stock_entry_with_material_request_items(pick_list): stock_entry = frappe.new_doc('Stock Entry') @@ -330,11 +277,99 @@ def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filte 'company': filters.get('company') }, as_dict=as_dict) -def get_item_details(item_code): - pass - @frappe.whitelist() +def target_document_exists(pick_list_name, purpose): + if purpose == 'Delivery against Sales Order': + return frappe.db.exists('Delivery Note', { + 'pick_list': pick_list_name + }) + + return stock_entry_exists(pick_list_name) + + +def update_delivery_note_item(source, target, delivery_note): + cost_center = frappe.db.get_value('Project', delivery_note.project, 'cost_center') + if not cost_center: + cost_center = get_cost_center(source.item_code, 'Item', delivery_note.company) + + if not cost_center: + cost_center = get_cost_center(source.item_group, 'Item Group', delivery_note.company) + + target.cost_center = cost_center + +def get_cost_center(for_item, from_doctype, company): + '''Returns Cost Center for Item or Item Group''' + return frappe.db.get_value('Item Default', + fieldname=['buying_cost_center'], + filters={ + 'parent': for_item, + 'parenttype': from_doctype, + 'company': company + }) + +def set_delivery_note_missing_values(target): + target.run_method('set_missing_values') + target.run_method('set_po_nos') + target.run_method('calculate_taxes_and_totals') + def stock_entry_exists(pick_list_name): return frappe.db.exists('Stock Entry', { 'pick_list': pick_list_name }) + +def get_item_details(item_code): + pass + +def update_stock_entry_based_on_work_order(pick_list, stock_entry): + work_order = frappe.get_doc("Work Order", pick_list.get('work_order')) + + stock_entry.work_order = work_order.name + stock_entry.company = work_order.company + stock_entry.from_bom = 1 + stock_entry.bom_no = work_order.bom_no + stock_entry.use_multi_level_bom = work_order.use_multi_level_bom + stock_entry.fg_completed_qty = pick_list.for_qty + if work_order.bom_no: + stock_entry.inspection_required = frappe.db.get_value('BOM', + work_order.bom_no, 'inspection_required') + + is_wip_warehouse_group = frappe.db.get_value('Warehouse', work_order.wip_warehouse, 'is_group') + if not (is_wip_warehouse_group and work_order.skip_transfer): + wip_warehouse = work_order.wip_warehouse + else: + wip_warehouse = None + stock_entry.to_warehouse = wip_warehouse + + stock_entry.project = work_order.project + + for location in pick_list.locations: + item = frappe._dict() + item.item_code = location.item_code + item.s_warehouse = location.warehouse + item.t_warehouse = wip_warehouse + item.qty = location.qty + item.transfer_qty = location.stock_qty + item.uom = location.uom + item.conversion_factor = location.conversion_factor + item.stock_uom = location.stock_uom + + stock_entry.append('items', item) + + return stock_entry + +def update_stock_entry_based_on_material_request(pick_list, stock_entry): + for location in pick_list.locations: + item = frappe._dict() + item.item_code = location.item_code + item.s_warehouse = location.warehouse + item.qty = location.qty + item.transfer_qty = location.stock_qty + item.uom = location.uom + item.conversion_factor = location.conversion_factor + item.stock_uom = location.stock_uom + item.material_request = location.material_request + item.material_request_item = location.material_request_item + + stock_entry.append('items', item) + + return stock_entry \ No newline at end of file From 05f243590d04a727dfd7d1e096f93bc6f3d092bd Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 26 Aug 2019 09:29:31 +0530 Subject: [PATCH 43/79] fix: Show option to create pick list or work order --- .../doctype/work_order/work_order.js | 64 +++++++++++-------- .../doctype/work_order/work_order.py | 8 ++- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index d345c0bf3f..c1f64ed0da 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -161,13 +161,6 @@ frappe.ui.form.on("Work Order", { frm.add_custom_button(__('Create BOM'), () => { frm.trigger("make_bom"); }); - - frm.add_custom_button(__('Pick List'), () => { - frappe.model.open_mapped_doc({ - method: "erpnext.manufacturing.doctype.work_order.work_order.make_pick_list", - frm - }); - }, __('Make')); } }, @@ -553,31 +546,46 @@ erpnext.work_order = { var max = (purpose === "Manufacture") ? flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty) : flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing); - } else { - var max = flt(frm.doc.qty) - flt(frm.doc.produced_qty); } + max = flt(max, precision('qty')); - max = flt(max, precision("qty")); - frappe.prompt({fieldtype:"Float", label: __("Qty for {0}", [purpose]), fieldname:"qty", - description: __("Max: {0}", [max]), 'default': max }, function(data) - { - if(data.qty > max) { - frappe.msgprint(__("Quantity must not be more than {0}", [max])); + frappe.prompt([{ + fieldtype: 'Float', + label: __('Qty for {0}', [purpose]), + fieldname: 'qty', + description: __('Max: {0}',[max]), + default: max + }, { + fieldtype: 'Select', + label: __('Create'), + fieldname: 'create', + default: 'Stock Entry', + options: 'Stock Entry\nPick List' + }], function (data) { + if (data.qty > max) { + frappe.msgprint(__('Quantity must not be more than {0}', [max])); return; } - frappe.call({ - method:"erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry", - args: { - "work_order_id": frm.doc.name, - "purpose": purpose, - "qty": data.qty - }, - callback: function(r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }); - }, __("Select Quantity"), __('Create')); + if (data.create === 'Stock Entry') { + frappe.xcall('erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry', { + 'work_order_id': frm.doc.name, + 'purpose': purpose, + 'qty': data.qty + }).then(stock_entry => { + frappe.model.sync(stock_entry); + frappe.set_route('Form', stock_entry.doctype, stock_entry.name); + }); + } else { + frappe.xcall('erpnext.manufacturing.doctype.work_order.work_order.create_pick_list', { + 'source_name': frm.doc.name, + 'for_qty': data.qty + }).then(pick_list => { + frappe.model.sync(pick_list); + frappe.set_route('Form', pick_list.doctype, pick_list.name); + }); + } + + }, __('Select Quantity'), __('Create')); }, make_consumption_se: function(frm, backflush_raw_materials_based_on) { diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 804d22ce6b..d3ce1b8533 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -710,15 +710,15 @@ def get_work_order_operation_data(work_order, operation, workstation): return d @frappe.whitelist() -def create_pick_list(source_name, target_doc=None): - pick_list = json.loads(target_doc) +def create_pick_list(source_name, target_doc=None, for_qty=None): + pick_list = for_qty or json.loads(target_doc).get('for_qty') max_finished_goods_qty = frappe.db.get_value('Work Order', source_name, 'qty') def update_item_quantity(source, target, source_parent): # qty = source.required_qty - source.transferred_qty # target.qty = qty pending_to_issue = flt(source.required_qty) - flt(source.transferred_qty) - desire_to_transfer = flt(source.required_qty) / max_finished_goods_qty * flt(pick_list.get('for_qty')) + desire_to_transfer = flt(source.required_qty) / max_finished_goods_qty * flt(for_qty) qty = 0 if desire_to_transfer <= pending_to_issue: @@ -762,4 +762,6 @@ def create_pick_list(source_name, target_doc=None): # doc.delete_key('items') # doc.set('items', item_map.values()) + doc.for_qty = for_qty + return doc \ No newline at end of file From 2b9e256002a5148ea9d4424bf786c7c48fab5d0c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 26 Aug 2019 10:05:01 +0530 Subject: [PATCH 44/79] fix: Rename method name --- erpnext/manufacturing/doctype/work_order/work_order.js | 4 ---- erpnext/selling/doctype/sales_order/sales_order.js | 6 +++--- erpnext/selling/doctype/sales_order/sales_order.py | 2 +- erpnext/stock/doctype/pick_list/pick_list.js | 2 +- erpnext/stock/doctype/pick_list/test_pick_list.py | 2 -- 5 files changed, 5 insertions(+), 11 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index c1f64ed0da..ce66c8fd15 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -264,10 +264,6 @@ frappe.ui.form.on("Work Order", { }); }, - make_pick_list() { - - }, - show_progress: function(frm) { var bars = []; var message = ''; diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 194971026d..6ffc6162d6 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -110,7 +110,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( let allow_delivery = false; if (doc.docstatus==1) { - this.frm.add_custom_button(__('Pick List'), () => this.make_pick_list(), __('Create')); + this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create')); if(this.frm.has_perm("submit")) { if(doc.status === 'On Hold') { @@ -235,9 +235,9 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( this.order_type(doc); }, - make_pick_list() { + create_pick_list() { frappe.model.open_mapped_doc({ - method: "erpnext.selling.doctype.sales_order.sales_order.make_pick_list", + method: "erpnext.selling.doctype.sales_order.sales_order.create_pick_list", frm: this.frm }) }, diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 426b00484c..a063061107 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1002,7 +1002,7 @@ def make_inter_company_purchase_order(source_name, target_doc=None): return make_inter_company_transaction("Sales Order", source_name, target_doc) @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): target.qty = flt(source.qty) - flt(source.delivered_qty) target.stock_qty = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.conversion_factor) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index a0082b859c..0a78faeb2b 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -118,7 +118,7 @@ frappe.ui.form.on('Pick List', { return; } erpnext.utils.map_current_doc({ - method: 'erpnext.selling.doctype.sales_order.sales_order.make_pick_list', + method: 'erpnext.selling.doctype.sales_order.sales_order.create_pick_list', source_doctype: 'Sales Order', target: frm, setters: { diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index df42ff1256..96b770d1de 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -7,8 +7,6 @@ import frappe import unittest # test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch'] -from erpnext.selling.doctype.sales_order.sales_order import make_pick_list - class TestPickList(unittest.TestCase): def test_pick_list_picks_warehouse_for_each_item(self): pick_list = frappe.get_doc({ From 457da47dba88e8c9c9f464b2a1da0164af87ed67 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 26 Aug 2019 10:05:35 +0530 Subject: [PATCH 45/79] fix: set for_qty instead of pick_list --- erpnext/manufacturing/doctype/work_order/work_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index d3ce1b8533..38e1157abc 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -711,7 +711,7 @@ def get_work_order_operation_data(work_order, operation, workstation): @frappe.whitelist() def create_pick_list(source_name, target_doc=None, for_qty=None): - pick_list = for_qty or json.loads(target_doc).get('for_qty') + for_qty = for_qty or json.loads(target_doc).get('for_qty') max_finished_goods_qty = frappe.db.get_value('Work Order', source_name, 'qty') def update_item_quantity(source, target, source_parent): # qty = source.required_qty - source.transferred_qty From 00f9930fbf6e656a1036ba7776dd71c64cc4a7c8 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 26 Aug 2019 10:09:07 +0530 Subject: [PATCH 46/79] style: Remove leftout print statements --- erpnext/stock/get_item_details.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 41101f4678..bf5a8180bd 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -46,14 +46,11 @@ def get_item_details(args, doc=None, overwrite_warehouse=True): """ args = process_args(args) - print('warehouse', args.warehouse, '========') item = frappe.get_cached_doc("Item", args.item_code) validate_item_details(args, item) out = get_basic_details(args, item, overwrite_warehouse) - print('warehouse2', out.warehouse, '========') - get_item_tax_template(args, item, out) out["item_tax_rate"] = get_item_tax_map(args.company, args.get("item_tax_template") if out.get("item_tax_template") is None \ else out.get("item_tax_template"), as_json=True) @@ -800,7 +797,6 @@ def get_projected_qty(item_code, warehouse): @frappe.whitelist() def get_bin_details(item_code, warehouse): - print(item_code, warehouse, '---------------------------') return frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, ["projected_qty", "actual_qty", "reserved_qty"], as_dict=True, cache=True) \ or {"projected_qty": 0, "actual_qty": 0, "reserved_qty": 0} From d6be8989e4d13f45383d919c1d816a8da7c1bc3c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 26 Aug 2019 22:11:49 +0530 Subject: [PATCH 47/79] fix: Remove "Material Issue" purpose from pick list - Because in material issue we already define source warehouse --- erpnext/stock/doctype/material_request/material_request.js | 1 - erpnext/stock/doctype/pick_list/pick_list.json | 4 ++-- erpnext/stock/doctype/pick_list/pick_list.py | 5 +++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index feaa4132b8..1027dd5fea 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -67,7 +67,6 @@ frappe.ui.form.on('Material Request', { } if (frm.doc.material_request_type === "Material Issue") { - add_create_pick_list_button(); frm.add_custom_button(__("Issue Material"), () => frm.events.make_stock_entry(frm), __('Create')); } diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index dbfd7d1bf2..c7aa4586bb 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -98,7 +98,7 @@ "fieldname": "purpose", "fieldtype": "Select", "label": "Purpose", - "options": "Material Transfer for Manufacture\nMaterial Issue\nMaterial Transfer\nDelivery against Sales Order" + "options": "Material Transfer for Manufacture\nMaterial Transfer\nDelivery against Sales Order" }, { "depends_on": "eval:['Material Transfer', 'Material Issue'].includes(doc.purpose)", @@ -115,7 +115,7 @@ } ], "is_submittable": 1, - "modified": "2019-08-26 06:52:04.532885", + "modified": "2019-08-26 16:11:03.184637", "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 16bb9e6dc7..d5bf64309d 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -359,9 +359,14 @@ def update_stock_entry_based_on_work_order(pick_list, stock_entry): def update_stock_entry_based_on_material_request(pick_list, stock_entry): for location in pick_list.locations: + target_warehouse = None + if location.material_request_item: + target_warehouse = frappe.get_value('Material Request Item', + location.material_request_item, 'warehouse') item = frappe._dict() item.item_code = location.item_code item.s_warehouse = location.warehouse + item.t_warehouse = target_warehouse item.qty = location.qty item.transfer_qty = location.stock_qty item.uom = location.uom From dc675b1eb0a0db97ed0ee6c1e8856d53bb70c58c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 27 Aug 2019 08:06:22 +0530 Subject: [PATCH 48/79] fix: Re-order stock qty field --- erpnext/stock/doctype/pick_list_item/pick_list_item.json | 4 ++-- .../pick_list_reference_item.json | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) 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 fdac9e58cf..809f6613a8 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -16,8 +16,8 @@ "picked_qty", "column_break_11", "uom", - "stock_uom", "conversion_factor", + "stock_uom", "serial_no_and_batch_section", "serial_no", "column_break_20", @@ -170,7 +170,7 @@ } ], "istable": 1, - "modified": "2019-08-26 06:54:06.783255", + "modified": "2019-08-26 22:19:56.872728", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", diff --git a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json index b549a08e10..0b3c4dc89f 100644 --- a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json +++ b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json @@ -10,8 +10,8 @@ "stock_qty", "column_break_5", "uom", - "stock_uom", "conversion_factor", + "stock_uom", "reference_section", "sales_order", "sales_order_item", @@ -33,6 +33,7 @@ { "fieldname": "stock_qty", "fieldtype": "Float", + "in_list_view": 1, "label": "Stock Qty" }, { @@ -45,7 +46,8 @@ "fieldname": "stock_uom", "fieldtype": "Link", "label": "Stock UOM", - "options": "UOM" + "options": "UOM", + "read_only": 1 }, { "fieldname": "item_code", @@ -96,7 +98,7 @@ } ], "istable": 1, - "modified": "2019-08-26 06:54:50.702552", + "modified": "2019-08-26 23:02:52.470206", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Reference Item", From 974ac3e6ade54f84f70010cc624cb73b276f6870 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 27 Aug 2019 10:17:16 +0530 Subject: [PATCH 49/79] fix: Update item detail for items without reference --- .../material_request/material_request.py | 1 - erpnext/stock/doctype/pick_list/pick_list.js | 50 +++++++++++++++++-- erpnext/stock/doctype/pick_list/pick_list.py | 11 +++- .../pick_list_item/pick_list_item.json | 3 +- .../pick_list_reference_item.json | 6 +-- 5 files changed, 59 insertions(+), 12 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 7040d83215..9cad3f039f 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -521,7 +521,6 @@ def create_pick_list(source_name, target_doc=None): 'name': 'material_request_item', 'qty': 'stock_qty' }, - # 'condition': lambda doc: abs(doc.transferred_qty) < abs(doc.required_qty) }, }, target_doc) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 0a78faeb2b..0d00024a34 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -27,7 +27,7 @@ frappe.ui.form.on('Pick List', { }; }); }, - get_item_locations(frm) { + get_item_locations: (frm) => { frm.call('set_item_locations'); }, refresh: (frm) => { @@ -84,18 +84,22 @@ frappe.ui.form.on('Pick List', { source_name: frm.doc.material_request }); }, + parent_warehouse: (frm) => { + frm.clear_table('locations'); + frm.refresh_field('locations'); + }, purpose: (frm) => { frm.clear_table('items'); frm.clear_table('locations'); frm.trigger('add_get_items_button'); }, - create_delivery_note(frm) { + create_delivery_note: (frm) => { frappe.model.open_mapped_doc({ method: 'erpnext.stock.doctype.pick_list.pick_list.create_delivery_note', frm: frm }); }, - create_stock_entry(frm) { + create_stock_entry: (frm) => { frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.create_stock_entry', { 'pick_list': frm.doc, }).then(stock_entry => { @@ -103,7 +107,7 @@ frappe.ui.form.on('Pick List', { frappe.set_route("Form", 'Stock Entry', stock_entry.name); }); }, - add_get_items_button(frm) { + add_get_items_button: (frm) => { let purpose = frm.doc.purpose; if (purpose != 'Delivery against Sales Order' || frm.doc.docstatus !== 0) return; let get_query_filters = { @@ -130,4 +134,40 @@ frappe.ui.form.on('Pick List', { }); }); } -}); \ No newline at end of file +}); + +frappe.ui.form.on('Pick List Reference Item', { + item_code: (frm, cdt, cdn) => { + let row = frappe.get_doc(cdt, cdn); + if (row.item_code) { + get_item_details(row.item_code).then(data => { + frappe.model.set_value(cdt, cdn, 'uom', data.stock_uom); + frappe.model.set_value(cdt, cdn, 'stock_uom', data.stock_uom); + frappe.model.set_value(cdt, cdn, 'conversion_factor', 1); + }); + } + }, + uom: (frm, cdt, cdn) => { + let row = frappe.get_doc(cdt, cdn); + if (row.uom) { + get_item_details(row.item_code, row.uom).then(data => { + frappe.model.set_value(cdt, cdn, 'conversion_factor', data.conversion_factor); + }); + } + }, + qty: (frm, cdt, cdn) => { + let row = frappe.get_doc(cdt, cdn); + frappe.model.set_value(cdt, cdn, 'stock_qty', row.qty * row.conversion_factor); + }, + conversion_factor: (frm, cdt, cdn) => { + let row = frappe.get_doc(cdt, cdn); + frappe.model.set_value(cdt, cdn, 'stock_qty', row.qty * row.conversion_factor); + } +}); + +function get_item_details(item_code, uom=null) { + return frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.get_item_details', { + item_code, + uom + }); +} \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index d5bf64309d..e12bd92c95 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -317,8 +317,15 @@ def stock_entry_exists(pick_list_name): 'pick_list': pick_list_name }) -def get_item_details(item_code): - pass +@frappe.whitelist() +def get_item_details(item_code, uom=None): + details = frappe.db.get_value('Item', item_code, ['stock_uom', 'name'], as_dict=1) + details.uom = uom or details.stock_uom + if uom: + details.update(get_conversion_factor(item_code, uom)) + + return details + def update_stock_entry_based_on_work_order(pick_list, stock_entry): work_order = frappe.get_doc("Work Order", pick_list.get('work_order')) 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 809f6613a8..947737719e 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -112,6 +112,7 @@ { "fieldname": "stock_qty", "fieldtype": "Float", + "in_list_view": 1, "label": "Qty as per Stock UOM", "read_only": 1 }, @@ -170,7 +171,7 @@ } ], "istable": 1, - "modified": "2019-08-26 22:19:56.872728", + "modified": "2019-08-27 08:44:04.173333", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", diff --git a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json index 0b3c4dc89f..861e6969d6 100644 --- a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json +++ b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json @@ -33,8 +33,8 @@ { "fieldname": "stock_qty", "fieldtype": "Float", - "in_list_view": 1, - "label": "Stock Qty" + "label": "Stock Qty", + "read_only": 1 }, { "fieldname": "uom", @@ -98,7 +98,7 @@ } ], "istable": 1, - "modified": "2019-08-26 23:02:52.470206", + "modified": "2019-08-27 08:40:21.870638", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Reference Item", From c5e0838890b95c977e6df4521e513dae3b3bb541 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 27 Aug 2019 12:14:17 +0530 Subject: [PATCH 50/79] fix: Import get_conversion factor method --- erpnext/stock/doctype/pick_list/pick_list.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index e12bd92c95..c7653438c1 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -10,6 +10,7 @@ from frappe.model.document import Document from frappe import _ from frappe.utils import floor, flt, today from frappe.model.mapper import get_mapped_doc, map_child_doc +from erpnext.stock.get_item_details import get_conversion_factor from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note as create_delivery_note_from_sales_order # TODO: Prioritize SO or WO group warehouse From cfec328c7490b12ec55a90cfb35edf0f1a360636 Mon Sep 17 00:00:00 2001 From: mbhavesh95863 <41165270+mbhavesh95863@users.noreply.github.com> Date: Tue, 27 Aug 2019 19:17:19 +0530 Subject: [PATCH 51/79] Same report 2 times in list --- erpnext/config/selling.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/erpnext/config/selling.py b/erpnext/config/selling.py index 844710d47c..928bd5fa32 100644 --- a/erpnext/config/selling.py +++ b/erpnext/config/selling.py @@ -304,12 +304,6 @@ def get_data(): "name": "Customers Without Any Sales Transactions", "doctype": "Customer" }, - { - "type": "report", - "is_query_report": True, - "name": "Sales Partners Commission", - "doctype": "Customer" - }, { "type": "report", "is_query_report": True, From 17d3de18212ae398495a53d6b074b4aa768ad448 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 27 Aug 2019 22:37:12 +0530 Subject: [PATCH 52/79] fix: process allocation expiry --- .../leave_ledger_entry/leave_ledger_entry.py | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index c82114e6d5..bfc6d95d17 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -95,26 +95,25 @@ def process_expired_allocation(): 'expire_carry_forwarded_leaves_after_days': (">", 0) }, fieldname=['name']) - if leave_type_records: - leave_type = [record[0] for record in leave_type_records] + leave_type = [record[0] for record in leave_type_records] - expired_allocation = frappe.db.sql_list("""SELECT name - FROM `tabLeave Ledger Entry` - WHERE - `transaction_type`='Leave Allocation' - AND `is_expired`=1""") + expired_allocation = frappe.db.sql_list("""SELECT name + FROM `tabLeave Ledger Entry` + WHERE + `transaction_type`='Leave Allocation' + AND `is_expired`=1""") - expire_allocation = frappe.get_all("Leave Ledger Entry", - fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], - filters={ - 'to_date': ("<", today()), - 'transaction_type': 'Leave Allocation', - 'transaction_name': ('not in', expired_allocation) - }, - or_filters={ - 'is_carry_forward': 0, - 'leave_type': ('in', leave_type) - }) + expire_allocation = frappe.get_all("Leave Ledger Entry", + fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], + filters={ + 'to_date': ("<", today()), + 'transaction_type': 'Leave Allocation', + 'transaction_name': ('not in', expired_allocation) + }, + or_filters={ + 'is_carry_forward': 0, + 'leave_type': ('in', leave_type) + }) if expire_allocation: create_expiry_ledger_entry(expire_allocation) From 8f32ac0eddb0ded74828043bd3fdf5a39e538e5d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 28 Aug 2019 10:16:20 +0530 Subject: [PATCH 53/79] fix: Batch selection logic --- .../stock/doctype/pick_list/pick_list.json | 4 +- erpnext/stock/doctype/pick_list/pick_list.py | 44 ++++++++++++------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index c7aa4586bb..b52e18974b 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -77,7 +77,7 @@ "options": "Pick List Item" }, { - "depends_on": "work_order", + "depends_on": "eval:doc.purpose==='Material Transfer for Manufacture'", "description": "Qty of raw materials will be decided based on the qty of the Finished Goods Item", "fieldname": "for_qty", "fieldtype": "Float", @@ -115,7 +115,7 @@ } ], "is_submittable": 1, - "modified": "2019-08-26 16:11:03.184637", + "modified": "2019-08-27 21:24:50.609986", "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 c7653438c1..11a998518a 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -55,13 +55,19 @@ class PickList(Document): 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 = [] + skip_warehouse = None + + if item_doc.material_request: + skip_warehouse = frappe.get_value('Material Request Item', item_doc.material_request_item, 'warehouse') + remaining_stock_qty = item_doc.stock_qty 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) - uom_must_be_whole_number = frappe.db.get_value("UOM", item_doc.uom, "must_be_whole_number") + uom_must_be_whole_number = frappe.db.get_value('UOM', item_doc.uom, 'must_be_whole_number') if uom_must_be_whole_number: qty = floor(qty) stock_qty = qty * item_doc.conversion_factor @@ -131,7 +137,7 @@ def get_item_locations_based_on_serial_nos(item_doc): return locations def get_item_locations_based_on_batch_nos(item_doc): - batch_qty = frappe.db.sql(""" + batch_locations = frappe.db.sql(""" SELECT sle.`warehouse`, sle.`batch_no`, @@ -141,27 +147,30 @@ def get_item_locations_based_on_batch_nos(item_doc): WHERE sle.batch_no = batch.name and sle.`item_code`=%(item_code)s - and IFNULL(batch.expiry_date, '2200-01-01') > %(today)s + and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s GROUP BY `warehouse`, `batch_no`, `item_code` HAVING `qty` > 0 - ORDER BY IFNULL(batch.expiry_date, '2200-01-01') + ORDER BY IFNULL(batch.`expiry_date`, '2200-01-01'), batch.`creation` """, { 'item_code': item_doc.item_code, 'today': today() }, as_dict=1) locations = [] - required_qty = item_doc.qty - for d in batch_qty: - if d.qty > required_qty: - d.qty = required_qty - else: - required_qty -= d.qty + required_qty = item_doc.stock_qty - locations.append(d) + for batch_location in batch_locations: + if batch_location.qty >= required_qty: + # this batch should fulfill the required items + batch_location.qty = required_qty + required_qty = 0 + else: + required_qty -= batch_location.qty + + locations.append(batch_location) if required_qty <= 0: break @@ -171,7 +180,6 @@ def get_item_locations_based_on_batch_nos(item_doc): return locations - @frappe.whitelist() def create_delivery_note(source_name, target_doc=None): pick_list = frappe.get_doc('Pick List', source_name) @@ -199,7 +207,9 @@ def create_delivery_note(source_name, target_doc=None): if dn_item: dn_item.warehouse = location.warehouse - dn_item.qty = location.qty + dn_item.qty = location.picked_qty + 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) @@ -355,8 +365,8 @@ def update_stock_entry_based_on_work_order(pick_list, stock_entry): item.item_code = location.item_code item.s_warehouse = location.warehouse item.t_warehouse = wip_warehouse - item.qty = location.qty - item.transfer_qty = location.stock_qty + item.qty = location.picked_qty * location.conversion_factor + item.transfer_qty = location.picked_qty item.uom = location.uom item.conversion_factor = location.conversion_factor item.stock_uom = location.stock_uom @@ -375,8 +385,8 @@ def update_stock_entry_based_on_material_request(pick_list, stock_entry): item.item_code = location.item_code item.s_warehouse = location.warehouse item.t_warehouse = target_warehouse - item.qty = location.qty - item.transfer_qty = location.stock_qty + item.qty = location.picked_qty * location.conversion_factor + item.transfer_qty = location.picked_qty item.uom = location.uom item.conversion_factor = location.conversion_factor item.stock_uom = location.stock_uom From 0aeccbf4b2ccecd71f43fd8b4d33fa6e300f5e82 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 28 Aug 2019 11:39:45 +0530 Subject: [PATCH 54/79] feat: added dashboard fixtures (#18812) * feat: add dashboard charts fixture * fix: remove return statement * feat: added patch for creating default dashboards * chore: renamed dashboard charts * feat: add add_dashboard function to install fixtures * fix: reload doctype issue in patches * fix (travis): reloaded dashboard chart source * fix (travis): reloaded dashboard chart source * fix (travis): reloaded dashboard doctype * fix (travis): reloaded dashboard chart link doctype --- erpnext/patches.txt | 5 + .../patches/v12_0/add_default_dashboards.py | 8 ++ .../setup_wizard/data/dashboard_charts.py | 107 ++++++++++++++++++ .../operations/install_fixtures.py | 10 +- 4 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v12_0/add_default_dashboards.py create mode 100644 erpnext/setup/setup_wizard/data/dashboard_charts.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index dc6c6daa13..07b8ee6ab4 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -625,5 +625,10 @@ erpnext.patches.v12_0.add_default_buying_selling_terms_in_company erpnext.patches.v12_0.update_ewaybill_field_position erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes erpnext.patches.v11_1.set_status_for_material_request_type_manufacture +execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_link') +execute:frappe.reload_doc('desk', 'doctype','dashboard') +execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_source') +execute:frappe.reload_doc('desk', 'doctype','dashboard_chart') +erpnext.patches.v12_0.add_default_dashboards erpnext.patches.v12_0.remove_bank_remittance_custom_fields erpnext.patches.v12_0.generate_leave_ledger_entries diff --git a/erpnext/patches/v12_0/add_default_dashboards.py b/erpnext/patches/v12_0/add_default_dashboards.py new file mode 100644 index 0000000000..ab92fbaa69 --- /dev/null +++ b/erpnext/patches/v12_0/add_default_dashboards.py @@ -0,0 +1,8 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +from erpnext.setup.setup_wizard.operations.install_fixtures import add_dashboards + +def execute(): + add_dashboards() diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py new file mode 100644 index 0000000000..41cfcb529e --- /dev/null +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -0,0 +1,107 @@ +from __future__ import unicode_literals +from frappe import _ +import frappe +import json + +def get_default_dashboards(): + company = frappe.get_doc("Company", frappe.defaults.get_defaults().company) + income_account = company.default_income_account or get_account("Income Account", company.name) + expense_account = company.default_expense_account or get_account("Expense Account", company.name) + bank_account = company.default_bank_account or get_account("Bank", company.name) + + return { + "Dashboards": [ + { + "doctype": "Dashboard", + "dashboard_name": "Accounts", + "charts": [ + { "chart": "Outgoing Bills (Sales Invoice)" }, + { "chart": "Incoming Bills (Purchase Invoice)" }, + { "chart": "Bank Balance" }, + { "chart": "Income" }, + { "chart": "Expenses" } + ] + } + ], + "Charts": [ + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "chart_name": "Income", + "timespan": "Last Year", + "color": None, + "filters_json": json.dumps({"company": company.name, "account": income_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "chart_name": "Expenses", + "timespan": "Last Year", + "color": None, + "filters_json": json.dumps({"company": company.name, "account": expense_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "chart_name": "Bank Balance", + "timespan": "Last Year", + "color": "#ffb868", + "filters_json": json.dumps({"company": company.name, "account": bank_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "chart_name": "Incoming Bills (Purchase Invoice)", + "timespan": "Last Year", + "color": "#a83333", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Purchase Invoice", + "type": "Bar", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "chart_name": "Outgoing Bills (Sales Invoice)", + "timespan": "Last Year", + "color": "#7b933d", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Sales Invoice", + "type": "Bar", + "width": "Half" + } + ] + } + +def get_account(account_type, company): + accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company}) + if accounts: + return accounts[0].name \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 4b734df61d..7123021d1e 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -475,13 +475,14 @@ def install_defaults(args=None): frappe.db.set_value("Company", args.company_name, "default_bank_account", bank_account.name, update_modified=False) - return doc except RootNotEditable: frappe.throw(_("Bank account cannot be named as {0}").format(args.bank_account)) except frappe.DuplicateEntryError: # bank account same as a CoA entry pass + add_dashboards() + # Now, with fixtures out of the way, onto concrete stuff records = [ @@ -499,6 +500,13 @@ def install_defaults(args=None): make_records(records) +def add_dashboards(): + from erpnext.setup.setup_wizard.data.dashboard_charts import get_default_dashboards + dashboard_data = get_default_dashboards() + + make_records(dashboard_data["Charts"]) + make_records(dashboard_data["Dashboards"]) + def get_fy_details(fy_start_date, fy_end_date): start_year = getdate(fy_start_date).year From be0ae7a4303d65310260ae698e75c9551ac71574 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 28 Aug 2019 12:40:26 +0530 Subject: [PATCH 55/79] fix: Doctype changes - Check no copy for work order series field - Add dashboard - Set naming_series for pick list --- erpnext/config/stock.py | 8 +++++++- .../doctype/work_order/work_order.json | 3 ++- erpnext/stock/doctype/pick_list/pick_list.js | 4 ++++ erpnext/stock/doctype/pick_list/pick_list.json | 15 ++++++++++++--- .../doctype/pick_list_item/pick_list_item.json | 4 ++-- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py index 7d66df2360..441a3ab4ec 100644 --- a/erpnext/config/stock.py +++ b/erpnext/config/stock.py @@ -30,6 +30,12 @@ def get_data(): "onboard": 1, "dependencies": ["Item"], }, + { + "type": "doctype", + "name": "Pick List", + "onboard": 1, + "dependencies": ["Item"], + }, { "type": "doctype", "name": "Delivery Trip" @@ -329,5 +335,5 @@ def get_data(): } ] }, - + ] diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index 63c95e7c21..0d073a2312 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -72,6 +72,7 @@ "fieldname": "naming_series", "fieldtype": "Select", "label": "Series", + "no_copy": 1, "options": "MFG-WO-.YYYY.-", "print_hide": 1, "reqd": 1, @@ -467,7 +468,7 @@ "idx": 1, "image_field": "image", "is_submittable": 1, - "modified": "2019-07-31 00:13:38.218277", + "modified": "2019-08-28 12:29:35.315239", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order", diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 0d00024a34..163ff8c2f2 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -3,6 +3,10 @@ frappe.ui.form.on('Pick List', { setup: (frm) => { + frm.custom_make_buttons = { + 'Delivery Note': 'Delivery Note', + 'Stock Entry': 'Stock Entry', + }; frm.set_query('parent_warehouse', () => { return { filters: { diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index b52e18974b..5d33378d69 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -1,10 +1,12 @@ { - "autoname": "PICK.####", + "autoname": "naming_series:", "creation": "2019-07-11 16:03:13.681045", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "naming_series", + "company", "purpose", "customer", "work_order", @@ -12,7 +14,6 @@ "for_qty", "column_break_4", "parent_warehouse", - "company", "section_break_4", "items", "get_item_locations", @@ -112,10 +113,18 @@ "fieldname": "get_item_locations", "fieldtype": "Button", "label": "Get Item Locations" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "STO-PICK-.YYYY.-", + "reqd": 1, + "set_only_once": 1 } ], "is_submittable": 1, - "modified": "2019-08-27 21:24:50.609986", + "modified": "2019-08-28 12:27:22.743705", "modified_by": "Administrator", "module": "Stock", "name": "Pick List", 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 947737719e..4dc3ae4e3a 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -113,7 +113,7 @@ "fieldname": "stock_qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Qty as per Stock UOM", + "label": "Qty (Stock UOM)", "read_only": 1 }, { @@ -171,7 +171,7 @@ } ], "istable": 1, - "modified": "2019-08-27 08:44:04.173333", + "modified": "2019-08-28 12:34:06.224534", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", From 1cf7270049b511fe6a4ce4d73479bc915dbf9d2e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 28 Aug 2019 12:40:58 +0530 Subject: [PATCH 56/79] fix: Add picklist dashboard config --- .../stock/doctype/pick_list/pick_list_dashboard.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 erpnext/stock/doctype/pick_list/pick_list_dashboard.py diff --git a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py new file mode 100644 index 0000000000..6e007df5e6 --- /dev/null +++ b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'pick_list', + 'transactions': [ + { + 'items': ['Stock Entry', 'Delivery Note'] + }, + ] + } \ No newline at end of file From 9fa5dbd6e4fea97b96059e0e1c37b77194aa1d06 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 28 Aug 2019 13:29:03 +0530 Subject: [PATCH 57/79] fix: raised exception in shopify --- .../doctype/shopify_settings/shopify_settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py index 7f4b7bab0a..f5191770b2 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py @@ -58,7 +58,7 @@ class ShopifySettings(Document): d.raise_for_status() self.update_webhook_table(method, d.json()) except Exception as e: - make_shopify_log(status="Warning", message=e.message, exception=False) + make_shopify_log(status="Warning", message=e, exception=False) def unregister_webhooks(self): session = get_request_session() @@ -71,7 +71,7 @@ class ShopifySettings(Document): res.raise_for_status() deleted_webhooks.append(d) except Exception as e: - frappe.log_error(message=frappe.get_traceback(), title=e.message[:140]) + frappe.log_error(message=frappe.get_traceback(), title=e[:140]) for d in deleted_webhooks: self.remove(d) From 1707c3da9598ee51124d0ba93069cf38408446a3 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 28 Aug 2019 13:55:26 +0530 Subject: [PATCH 58/79] fix: exception Co-Authored-By: Himanshu --- .../doctype/shopify_settings/shopify_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py index f5191770b2..4943053663 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py @@ -71,7 +71,7 @@ class ShopifySettings(Document): res.raise_for_status() deleted_webhooks.append(d) except Exception as e: - frappe.log_error(message=frappe.get_traceback(), title=e[:140]) + frappe.log_error(message=frappe.get_traceback(), title=e) for d in deleted_webhooks: self.remove(d) From d03d8204ab797e2b4cb3a46e4fda64a5e4f91f1e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 28 Aug 2019 16:56:07 +0530 Subject: [PATCH 59/79] fix: incorrect valuation rate calculated because of string replacement issue --- erpnext/stock/stock_ledger.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 920fc272f7..f7deac3591 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -227,9 +227,9 @@ class update_entries_after(object): elif actual_qty < 0: # In case of delivery/stock issue, get average purchase rate # of serial nos of current entry - stock_value_change = -1 * flt(frappe.db.sql("""select sum(purchase_rate) - from `tabSerial No` where name in (%s)""" % (", ".join(["%s"]*len(serial_no))), - tuple(serial_no))[0][0]) + stock_value_change = -1 * flt(frappe.get_all("Serial No", + fields=["sum(purchase_rate)"], + filters = {'name': ('in', serial_no)}, as_list=1)[0][0]) new_stock_qty = self.qty_after_transaction + actual_qty From f455c92d02554103e91025705c5758c9d5d96a5b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 28 Aug 2019 17:44:22 +0530 Subject: [PATCH 60/79] fix: Remove Pick List Reference Item use Item locations table instead --- .../doctype/work_order/work_order.py | 33 ++---- .../doctype/sales_order/sales_order.py | 26 +++-- .../material_request/material_request.py | 4 +- erpnext/stock/doctype/pick_list/pick_list.js | 11 +- .../stock/doctype/pick_list/pick_list.json | 17 +-- erpnext/stock/doctype/pick_list/pick_list.py | 48 +++----- .../pick_list_item/pick_list_item.json | 11 +- .../pick_list_reference_item/__init__.py | 0 .../pick_list_reference_item.js | 8 -- .../pick_list_reference_item.json | 109 ------------------ .../pick_list_reference_item.py | 10 -- 11 files changed, 49 insertions(+), 228 deletions(-) delete mode 100644 erpnext/stock/doctype/pick_list_reference_item/__init__.py delete mode 100644 erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.js delete mode 100644 erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json delete mode 100644 erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.py diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 38e1157abc..efa901b7f9 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -735,33 +735,22 @@ def create_pick_list(source_name, target_doc=None, for_qty=None): else: target.delete() - doc = get_mapped_doc("Work Order", source_name, { - "Work Order": { - "doctype": "Pick List", - "validation": { - "docstatus": ["=", 1] + doc = get_mapped_doc('Work Order', source_name, { + 'Work Order': { + 'doctype': 'Pick List', + 'validation': { + 'docstatus': ['=', 1] } }, - "Work Order Item": { - "doctype": "Pick List Reference Item", - "postprocess": update_item_quantity, - "condition": lambda doc: abs(doc.transferred_qty) < abs(doc.required_qty) + 'Work Order Item': { + 'doctype': 'Pick List Item', + 'postprocess': update_item_quantity, + 'condition': lambda doc: abs(doc.transferred_qty) < abs(doc.required_qty) }, }, target_doc) - # # aggregate qty for same item - # item_map = frappe._dict() - # for item in doc.items: - # item.idx = None - # if not item_map.get(item.item_code): - # item_map[item.item_code] = item - # else: - # item_map[item.item_code].qty += item.qty - # item_map[item.item_code].stock_qty += item.stock_qty - - # doc.delete_key('items') - # doc.set('items', item_map.values()) - doc.for_qty = for_qty + doc.set_item_locations() + return doc \ 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 e6621b1260..c9aaab4b21 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -998,24 +998,26 @@ def create_pick_list(source_name, target_doc=None): target.qty = flt(source.qty) - flt(source.delivered_qty) target.stock_qty = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.conversion_factor) - doc = get_mapped_doc("Sales Order", source_name, { - "Sales Order": { - "doctype": "Pick List", - "validation": { - "docstatus": ["=", 1] + doc = get_mapped_doc('Sales Order', source_name, { + 'Sales Order': { + 'doctype': 'Pick List', + 'validation': { + 'docstatus': ['=', 1] } }, - "Sales Order Item": { - "doctype": "Pick List Reference Item", - "field_map": { - "parent": "sales_order", - "name": "sales_order_item" + 'Sales Order Item': { + '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 + 'postprocess': update_item_quantity, + 'condition': lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 }, }, target_doc) doc.purpose = 'Delivery against Sales Order' + doc.set_item_locations() + return doc diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 9cad3f039f..44e890cc50 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -516,7 +516,7 @@ def create_pick_list(source_name, target_doc=None): } }, 'Material Request Item': { - 'doctype': 'Pick List Reference Item', + 'doctype': 'Pick List Item', 'field_map': { 'name': 'material_request_item', 'qty': 'stock_qty' @@ -524,4 +524,6 @@ def create_pick_list(source_name, target_doc=None): }, }, target_doc) + doc.set_item_locations() + return doc \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 163ff8c2f2..54a2ca9f0b 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -30,6 +30,7 @@ frappe.ui.form.on('Pick List', { } }; }); + // frm.set_df_property('get_item_locations', 'hidden', frm.is_dirty()); }, get_item_locations: (frm) => { frm.call('set_item_locations'); @@ -69,7 +70,6 @@ frappe.ui.form.on('Pick List', { frappe.msgprint(__('Quantity must not be more than {0}', [max])); return; } - frm.clear_table('items'); frm.clear_table('locations'); erpnext.utils.map_current_doc({ method: 'erpnext.manufacturing.doctype.work_order.work_order.create_pick_list', @@ -80,20 +80,13 @@ frappe.ui.form.on('Pick List', { }); }, material_request: (frm) => { - frm.clear_table('items'); - frm.clear_table('locations'); erpnext.utils.map_current_doc({ method: 'erpnext.stock.doctype.material_request.material_request.create_pick_list', target: frm, source_name: frm.doc.material_request }); }, - parent_warehouse: (frm) => { - frm.clear_table('locations'); - frm.refresh_field('locations'); - }, purpose: (frm) => { - frm.clear_table('items'); frm.clear_table('locations'); frm.trigger('add_get_items_button'); }, @@ -140,7 +133,7 @@ frappe.ui.form.on('Pick List', { } }); -frappe.ui.form.on('Pick List Reference Item', { +frappe.ui.form.on('Pick List Item', { item_code: (frm, cdt, cdn) => { let row = frappe.get_doc(cdt, cdn); if (row.item_code) { diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 5d33378d69..e635db0540 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -14,8 +14,6 @@ "for_qty", "column_break_4", "parent_warehouse", - "section_break_4", - "items", "get_item_locations", "section_break_6", "locations", @@ -38,10 +36,6 @@ "fieldname": "section_break_6", "fieldtype": "Section Break" }, - { - "fieldname": "section_break_4", - "fieldtype": "Section Break" - }, { "description": "Items under this warehouse will be suggested", "fieldname": "parent_warehouse", @@ -64,13 +58,6 @@ "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", @@ -109,7 +96,6 @@ "options": "Material Request" }, { - "depends_on": "eval: doc.items.length && (doc.items.length > 1 || doc.items[0].item_code) && doc.docstatus === 0", "fieldname": "get_item_locations", "fieldtype": "Button", "label": "Get Item Locations" @@ -124,7 +110,7 @@ } ], "is_submittable": 1, - "modified": "2019-08-28 12:27:22.743705", + "modified": "2019-08-28 12:49:59.800807", "modified_by": "Administrator", "module": "Stock", "name": "Pick List", @@ -191,7 +177,6 @@ "write": 1 } ], - "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 11a998518a..0932c5ad06 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -17,7 +17,7 @@ from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note a class PickList(Document): def set_item_locations(self): - items = self.items + item_locations = self.locations self.item_location_map = frappe._dict() from_warehouses = None @@ -26,7 +26,7 @@ class PickList(Document): # Reset self.delete_key('locations') - for item_doc in items: + for item_doc in item_locations: 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) @@ -37,20 +37,21 @@ class PickList(Document): 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) + # hack + del item_doc.idx + if len(locations) > 1: + del item_doc.name + for row in locations: + stock_qty = row.get('qty', 0) * item_doc.conversion_factor row.update({ - 'item_code': item_code, - 'sales_order': item_doc.sales_order, - 'sales_order_item': item_doc.sales_order_item, - 'material_request': item_doc.material_request, - 'material_request_item': item_doc.material_request_item, - 'uom': item_doc.uom, - 'stock_uom': item_doc.stock_uom, - 'conversion_factor': item_doc.conversion_factor, - 'stock_qty': row.get("qty", 0) * item_doc.conversion_factor, - 'picked_qty': row.get("qty", 0) * item_doc.conversion_factor + 'stock_qty': stock_qty, + 'picked_qty': stock_qty }) - self.append('locations', row) + + location = item_doc + location.update(row) + self.append('locations', location) def get_items_with_warehouse_and_quantity(item_doc, from_warehouses, item_location_map): available_locations = item_location_map.get(item_doc.item_code) @@ -242,27 +243,6 @@ def create_stock_entry(pick_list): return stock_entry.as_dict() -@frappe.whitelist() -def create_stock_entry_with_material_request_items(pick_list): - stock_entry = frappe.new_doc('Stock Entry') - stock_entry.pick_list = pick_list.get('name') - stock_entry.purpose = pick_list.get('purpose') - stock_entry.set_stock_entry_type() - - doc = get_mapped_doc("Work Order", source_name, { - "Work Order": { - "doctype": "Pick List", - "validation": { - "docstatus": ["=", 1] - } - }, - "Work Order Item": { - "doctype": "Pick List Reference Item", - "postprocess": update_item_quantity, - "condition": lambda doc: abs(doc.transferred_qty) < abs(doc.required_qty) - }, - }, target_doc) - @frappe.whitelist() def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filters, as_dict): return frappe.db.sql(""" 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 4dc3ae4e3a..552764dc2f 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -33,8 +33,7 @@ "fieldname": "qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Qty", - "read_only": 1 + "label": "Qty" }, { "fieldname": "picked_qty", @@ -100,8 +99,7 @@ "fieldname": "uom", "fieldtype": "Link", "label": "UOM", - "options": "UOM", - "read_only": 1 + "options": "UOM" }, { "fieldname": "conversion_factor", @@ -121,8 +119,7 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Item", - "options": "Item", - "read_only": 1 + "options": "Item" }, { "fieldname": "quantity_section", @@ -171,7 +168,7 @@ } ], "istable": 1, - "modified": "2019-08-28 12:34:06.224534", + "modified": "2019-08-28 15:56:14.507776", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", diff --git a/erpnext/stock/doctype/pick_list_reference_item/__init__.py b/erpnext/stock/doctype/pick_list_reference_item/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.js b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.js deleted file mode 100644 index 875ce23a28..0000000000 --- a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Pick List Reference Item', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json deleted file mode 100644 index 861e6969d6..0000000000 --- a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "creation": "2019-07-24 16:11:07.415562", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "item_code", - "quantity_section", - "qty", - "stock_qty", - "column_break_5", - "uom", - "conversion_factor", - "stock_uom", - "reference_section", - "sales_order", - "sales_order_item", - "material_request", - "material_request_item" - ], - "fields": [ - { - "fieldname": "qty", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Qty" - }, - { - "fieldname": "quantity_section", - "fieldtype": "Section Break", - "label": "Quantity" - }, - { - "fieldname": "stock_qty", - "fieldtype": "Float", - "label": "Stock Qty", - "read_only": 1 - }, - { - "fieldname": "uom", - "fieldtype": "Link", - "label": "UOM", - "options": "UOM" - }, - { - "fieldname": "stock_uom", - "fieldtype": "Link", - "label": "Stock UOM", - "options": "UOM", - "read_only": 1 - }, - { - "fieldname": "item_code", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Item", - "options": "Item" - }, - { - "fieldname": "column_break_5", - "fieldtype": "Column Break" - }, - { - "fieldname": "reference_section", - "fieldtype": "Section Break", - "label": "Reference" - }, - { - "fieldname": "sales_order", - "fieldtype": "Link", - "label": "Sales Order", - "options": "Sales Order", - "read_only": 1 - }, - { - "fieldname": "sales_order_item", - "fieldtype": "Data", - "label": "Sales Order Item", - "read_only": 1 - }, - { - "fieldname": "conversion_factor", - "fieldtype": "Float", - "label": "UOM Conversion Factor" - }, - { - "fieldname": "material_request", - "fieldtype": "Link", - "label": "Material Request", - "options": "Material Request", - "read_only": 1 - }, - { - "fieldname": "material_request_item", - "fieldtype": "Data", - "label": "Material Request Item", - "read_only": 1 - } - ], - "istable": 1, - "modified": "2019-08-27 08:40:21.870638", - "modified_by": "Administrator", - "module": "Stock", - "name": "Pick List Reference Item", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.py b/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.py deleted file mode 100644 index 74f0563cdf..0000000000 --- a/erpnext/stock/doctype/pick_list_reference_item/pick_list_reference_item.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 PickListReferenceItem(Document): - pass From 354d0af44e138b9bb84c36bbbd51448ccce56af3 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 28 Aug 2019 17:51:31 +0530 Subject: [PATCH 61/79] fix: added purchase invoice in buying module --- erpnext/config/buying.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index f5d8da74c5..6f5ab32b63 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -14,6 +14,12 @@ def get_data(): "dependencies": ["Item", "Supplier"], "description": _("Purchase Orders given to Suppliers."), }, + { + "type": "doctype", + "name": "Purchase Invoice", + "onboard": 1, + "dependencies": ["Item", "Supplier"] + }, { "type": "doctype", "name": "Material Request", From 55a2699f4c497ac8c622fb56da078ac58a9ec4c1 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 29 Aug 2019 09:14:27 +0530 Subject: [PATCH 62/79] fix: Update dashboard of doctypes that are linked with pick list --- .../manufacturing/doctype/work_order/work_order_dashboard.py | 2 +- erpnext/selling/doctype/sales_order/sales_order.js | 1 + erpnext/selling/doctype/sales_order/sales_order_dashboard.py | 2 +- erpnext/stock/doctype/material_request/material_request.js | 1 + .../doctype/material_request/material_request_dashboard.py | 2 +- 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py index 3fe5282582..0d3c30ea6e 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py +++ b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py @@ -6,7 +6,7 @@ def get_data(): 'fieldname': 'work_order', 'transactions': [ { - 'items': ['Stock Entry', 'Job Card'] + 'items': ['Pick List', 'Stock Entry', 'Job Card'] } ] } \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 6ffc6162d6..e86caddb87 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -7,6 +7,7 @@ frappe.ui.form.on("Sales Order", { setup: function(frm) { frm.custom_make_buttons = { 'Delivery Note': 'Delivery', + 'Pick List': 'Pick List', 'Sales Invoice': 'Invoice', 'Material Request': 'Material Request', 'Purchase Order': 'Purchase Order', diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py index aab6db2584..4126bc6a70 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py +++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py @@ -17,7 +17,7 @@ def get_data(): 'transactions': [ { 'label': _('Fulfillment'), - 'items': ['Sales Invoice', 'Delivery Note'] + 'items': ['Sales Invoice', 'Pick List', 'Delivery Note'] }, { 'label': _('Purchasing'), diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 1027dd5fea..99195c300d 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -8,6 +8,7 @@ frappe.ui.form.on('Material Request', { setup: function(frm) { frm.custom_make_buttons = { 'Stock Entry': 'Issue Material', + 'Pick List': 'Pick List', 'Purchase Order': 'Purchase Order', 'Request for Quotation': 'Request for Quotation', 'Supplier Quotation': 'Supplier Quotation', diff --git a/erpnext/stock/doctype/material_request/material_request_dashboard.py b/erpnext/stock/doctype/material_request/material_request_dashboard.py index adfab86cc4..cbd64784c6 100644 --- a/erpnext/stock/doctype/material_request/material_request_dashboard.py +++ b/erpnext/stock/doctype/material_request/material_request_dashboard.py @@ -8,7 +8,7 @@ def get_data(): 'transactions': [ { 'label': _('Related'), - 'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order', "Stock Entry"] + 'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order', 'Stock Entry', 'Pick List'] }, { 'label': _('Manufacturing'), From 66f2bc69a637c6795547f595785162453d916be7 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 29 Aug 2019 09:23:31 +0530 Subject: [PATCH 63/79] fix: Item mapping of stock entry with no reference --- erpnext/stock/doctype/pick_list/pick_list.py | 27 ++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 0932c5ad06..2f276e2f76 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -37,10 +37,8 @@ class PickList(Document): 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) - # hack - del item_doc.idx - if len(locations) > 1: - del item_doc.name + item_doc.idx = None + item_doc.name = None for row in locations: stock_qty = row.get('qty', 0) * item_doc.conversion_factor @@ -49,7 +47,7 @@ class PickList(Document): 'picked_qty': stock_qty }) - location = item_doc + location = item_doc.as_dict() location.update(row) self.append('locations', location) @@ -236,6 +234,8 @@ def create_stock_entry(pick_list): stock_entry = update_stock_entry_based_on_work_order(pick_list, stock_entry) elif pick_list.get('material_request'): stock_entry = update_stock_entry_based_on_material_request(pick_list, stock_entry) + else: + stock_entry = update_stock_entry_items_with_no_reference(pick_list, stock_entry) stock_entry.set_incoming_rate() stock_entry.set_actual_qty() @@ -375,4 +375,21 @@ def update_stock_entry_based_on_material_request(pick_list, stock_entry): stock_entry.append('items', item) + return stock_entry + +def update_stock_entry_items_with_no_reference(pick_list, stock_entry): + for location in pick_list.locations: + item = frappe._dict() + item.item_code = location.item_code + item.s_warehouse = location.warehouse + item.qty = location.picked_qty * location.conversion_factor + item.transfer_qty = location.picked_qty + item.uom = location.uom + item.conversion_factor = location.conversion_factor + item.stock_uom = location.stock_uom + item.material_request = location.material_request + item.material_request_item = location.material_request_item + + stock_entry.append('items', item) + return stock_entry \ No newline at end of file From ed1ec82d2fb53d7ac81e2a1486050aece83419dc Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 29 Aug 2019 17:23:09 +0530 Subject: [PATCH 64/79] refactor: Item picking logic - Fix serial number selection - Get limited item location based on required qty - Store total item count to properly track available locations - Pass missing serial no. --- erpnext/stock/doctype/pick_list/pick_list.py | 205 ++++++++++--------- 1 file changed, 113 insertions(+), 92 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 2f276e2f76..21b9cc1054 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -8,7 +8,7 @@ import json from six import iteritems from frappe.model.document import Document from frappe import _ -from frappe.utils import floor, flt, today +from frappe.utils import floor, flt, today, cint from frappe.model.mapper import get_mapped_doc, map_child_doc from erpnext.stock.get_item_details import get_conversion_factor from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note as create_delivery_note_from_sales_order @@ -17,51 +17,69 @@ from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note a class PickList(Document): def set_item_locations(self): - item_locations = self.locations + items = self.aggregate_item_qty() self.item_location_map = frappe._dict() from_warehouses = None if self.parent_warehouse: from_warehouses = frappe.db.get_descendants('Warehouse', self.parent_warehouse) - # Reset + # reset self.delete_key('locations') - for item_doc in item_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: - 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) + + self.item_location_map.setdefault(item_code, + get_available_item_locations(item_code, from_warehouses, self.item_count_map.get(item_code))) + + locations = get_items_with_location_and_quantity(item_doc, self.item_location_map) item_doc.idx = None item_doc.name = None for row in locations: - stock_qty = row.get('qty', 0) * item_doc.conversion_factor row.update({ - 'stock_qty': stock_qty, - 'picked_qty': stock_qty + 'picked_qty': row.stock_qty }) location = item_doc.as_dict() location.update(row) self.append('locations', location) -def get_items_with_warehouse_and_quantity(item_doc, from_warehouses, item_location_map): + def aggregate_item_qty(self): + locations = self.locations + self.item_count_map = {} + # aggregate qty for same item + item_map = frappe._dict() + for item in locations: + item_code = item.item_code + reference = item.sales_order_item or item.material_request_item + key = (item_code, item.uom, reference) + + item.idx = None + item.name = None + + if item_map.get(key): + item_map[key].qty += item.qty + item_map[key].stock_qty += item.stock_qty + else: + item_map[key] = item + + # maintain count of each item (useful to limit get query) + self.item_count_map.setdefault(item_code, 0) + self.item_count_map[item_code] += item.stock_qty + + return item_map.values() + + +def get_items_with_location_and_quantity(item_doc, item_location_map): available_locations = item_location_map.get(item_doc.item_code) locations = [] - skip_warehouse = None - - if item_doc.material_request: - skip_warehouse = frappe.get_value('Material Request Item', item_doc.material_request_item, 'warehouse') remaining_stock_qty = item_doc.stock_qty while remaining_stock_qty > 0 and available_locations: item_location = available_locations.pop(0) + item_location = frappe._dict(item_location) 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) @@ -72,54 +90,61 @@ def get_items_with_warehouse_and_quantity(item_doc, from_warehouses, item_locati stock_qty = qty * item_doc.conversion_factor if not stock_qty: break - locations.append({ + serial_nos = None + if item_location.serial_no: + serial_nos = '\n'.join(item_location.serial_no[0: cint(stock_qty)]) + + locations.append(frappe._dict({ 'qty': qty, - 'warehouse': item_location.warehouse - }) + 'stock_qty': stock_qty, + 'warehouse': item_location.warehouse, + 'serial_no': serial_nos, + 'batch_no': item_location.batch_no + })) + 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: + if qty_diff > 0: item_location.qty = qty_diff + if item_location.serial_no: + # set remaining serial numbers + item_location.serial_no = item_location.serial_no[-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)) - # 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 +def get_available_item_locations(item_code, from_warehouses, required_qty): + if frappe.get_cached_value('Item', item_code, 'has_serial_no'): + return get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty) + elif frappe.get_cached_value('Item', item_code, 'has_batch_no'): + return get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty) + else: + return get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty) + +def get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty): filters = frappe._dict({ 'item_code': item_code, - 'actual_qty': ['>', 0] + 'warehouse': ['!=', ''] }) + if from_warehouses: filters.warehouse = ['in', from_warehouses] - available_items = frappe.get_all('Bin', - fields=['warehouse', 'actual_qty as qty'], - filters=filters, - order_by='creation') - - return available_items - -def get_item_locations_based_on_serial_nos(item_doc): serial_nos = frappe.get_all('Serial No', - fields = ['name', 'warehouse'], - filters = { - 'item_code': item_doc.item_code, - 'warehouse': ['!=', ''] - }, limit=item_doc.stock_qty, order_by='purchase_date', as_list=1) + fields=['name', 'warehouse'], + filters=filters, + limit=required_qty, + order_by='purchase_date', + as_list=1) - remaining_stock_qty = flt(item_doc.stock_qty) - len(serial_nos) + remaining_stock_qty = required_qty - len(serial_nos) if remaining_stock_qty: - frappe.msgprint('{0} {1} of {2} is not available.' - .format(remaining_stock_qty, item_doc.stock_uom, item_doc.item_code)) + frappe.msgprint('{0} qty of {1} is not available.' + .format(remaining_stock_qty, item_code)) warehouse_serial_nos_map = frappe._dict() for serial_no, warehouse in serial_nos: @@ -130,12 +155,12 @@ def get_item_locations_based_on_serial_nos(item_doc): locations.append({ 'qty': len(serial_nos), 'warehouse': warehouse, - 'serial_no': '\n'.join(serial_nos) + 'serial_no': serial_nos }) return locations -def get_item_locations_based_on_batch_nos(item_doc): +def get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty): batch_locations = frappe.db.sql(""" SELECT sle.`warehouse`, @@ -154,30 +179,36 @@ def get_item_locations_based_on_batch_nos(item_doc): HAVING `qty` > 0 ORDER BY IFNULL(batch.`expiry_date`, '2200-01-01'), batch.`creation` """, { - 'item_code': item_doc.item_code, + 'item_code': item_code, 'today': today() }, as_dict=1) - locations = [] - required_qty = item_doc.stock_qty + total_qty_available = sum(location.get('qty') for location in batch_locations) - for batch_location in batch_locations: - if batch_location.qty >= required_qty: - # this batch should fulfill the required items - batch_location.qty = required_qty - required_qty = 0 - else: - required_qty -= batch_location.qty + remaining_qty = required_qty - total_qty_available - locations.append(batch_location) + if remaining_qty > 0: + frappe.msgprint('No batches found for {} qty of {}.'.format(remaining_qty, item_code)) - if required_qty <= 0: - break + return batch_locations - if required_qty: - frappe.msgprint('No batches found for {} qty of {}.'.format(required_qty, item_doc.item_code)) +def get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty): + # gets all items available in different warehouses + filters = frappe._dict({ + 'item_code': item_code, + 'actual_qty': ['>', 0] + }) - return locations + if from_warehouses: + filters.warehouse = ['in', from_warehouses] + + item_locations = frappe.get_all('Bin', + fields=['warehouse', 'actual_qty as qty'], + filters=filters, + limit=required_qty, + order_by='creation') + + return item_locations @frappe.whitelist() def create_delivery_note(source_name, target_doc=None): @@ -342,14 +373,8 @@ def update_stock_entry_based_on_work_order(pick_list, stock_entry): for location in pick_list.locations: item = frappe._dict() - item.item_code = location.item_code - item.s_warehouse = location.warehouse + update_common_item_properties(item, location) item.t_warehouse = wip_warehouse - item.qty = location.picked_qty * location.conversion_factor - item.transfer_qty = location.picked_qty - item.uom = location.uom - item.conversion_factor = location.conversion_factor - item.stock_uom = location.stock_uom stock_entry.append('items', item) @@ -362,17 +387,8 @@ def update_stock_entry_based_on_material_request(pick_list, stock_entry): target_warehouse = frappe.get_value('Material Request Item', location.material_request_item, 'warehouse') item = frappe._dict() - item.item_code = location.item_code - item.s_warehouse = location.warehouse + update_common_item_properties(item, location) item.t_warehouse = target_warehouse - item.qty = location.picked_qty * location.conversion_factor - item.transfer_qty = location.picked_qty - item.uom = location.uom - item.conversion_factor = location.conversion_factor - item.stock_uom = location.stock_uom - item.material_request = location.material_request - item.material_request_item = location.material_request_item - stock_entry.append('items', item) return stock_entry @@ -380,16 +396,21 @@ def update_stock_entry_based_on_material_request(pick_list, stock_entry): def update_stock_entry_items_with_no_reference(pick_list, stock_entry): for location in pick_list.locations: item = frappe._dict() - item.item_code = location.item_code - item.s_warehouse = location.warehouse - item.qty = location.picked_qty * location.conversion_factor - item.transfer_qty = location.picked_qty - item.uom = location.uom - item.conversion_factor = location.conversion_factor - item.stock_uom = location.stock_uom - item.material_request = location.material_request - item.material_request_item = location.material_request_item + update_common_item_properties(item, location) stock_entry.append('items', item) - return stock_entry \ No newline at end of file + return stock_entry + +def update_common_item_properties(item, location): + item.item_code = location.item_code + item.s_warehouse = location.warehouse + item.qty = location.picked_qty * location.conversion_factor + item.transfer_qty = location.picked_qty + item.uom = location.uom + item.conversion_factor = location.conversion_factor + item.stock_uom = location.stock_uom + item.material_request = location.material_request + item.serial_no = location.serial_no + item.batch_no = location.batch_no + item.material_request_item = location.material_request_item \ No newline at end of file From 3e11338ed55898ec4f7728a03fb1d6f1ff5e258b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 29 Aug 2019 19:46:49 +0530 Subject: [PATCH 65/79] fix: Validate count of serial numbers --- erpnext/stock/doctype/pick_list/pick_list.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 21b9cc1054..efe6da8e15 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -16,6 +16,15 @@ from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note a # TODO: Prioritize SO or WO group warehouse class PickList(Document): + def before_submit(self): + for item in self.locations: + if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'): + continue + if len(item.serial_no.split('\n')) == item.picked_qty: + continue + 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): items = self.aggregate_item_qty() self.item_location_map = frappe._dict() From 13c7e183a656cd48e60cb176777b68d684d4b0b9 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 29 Aug 2019 19:45:19 +0530 Subject: [PATCH 66/79] fix: not able to save sales order --- erpnext/controllers/selling_controller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 52d58dce62..9dbd5be918 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -394,8 +394,8 @@ class SellingController(StockController): elif self.doctype == "Delivery Note": e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or ''] f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice] - elif self.doctype == "Sales Order": - e = [d.item_code, d.description, d.warehouse, d.batch_no or ''] + elif self.doctype in ["Sales Order", "Quotation"]: + e = [d.item_code, d.description, d.warehouse, ''] f = [d.item_code, d.description] if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1: From a6ab00162e8694946a9e33fbd8c1820bd817175d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 29 Aug 2019 20:17:04 +0530 Subject: [PATCH 67/79] style: Remove commented code --- erpnext/manufacturing/doctype/work_order/work_order.py | 3 --- erpnext/stock/doctype/pick_list/pick_list.js | 1 - 2 files changed, 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index efa901b7f9..a636b871e2 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -714,9 +714,6 @@ def create_pick_list(source_name, target_doc=None, for_qty=None): for_qty = for_qty or json.loads(target_doc).get('for_qty') max_finished_goods_qty = frappe.db.get_value('Work Order', source_name, 'qty') def update_item_quantity(source, target, source_parent): - # qty = source.required_qty - source.transferred_qty - # target.qty = qty - pending_to_issue = flt(source.required_qty) - flt(source.transferred_qty) desire_to_transfer = flt(source.required_qty) / max_finished_goods_qty * flt(for_qty) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 54a2ca9f0b..4b8df0939e 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -30,7 +30,6 @@ frappe.ui.form.on('Pick List', { } }; }); - // frm.set_df_property('get_item_locations', 'hidden', frm.is_dirty()); }, get_item_locations: (frm) => { frm.call('set_item_locations'); From 9b42682575fa55fdda507670d70db9b3c4d52da9 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 29 Aug 2019 21:14:26 +0530 Subject: [PATCH 68/79] fix: Check parent warehouse for batch as well --- erpnext/stock/doctype/pick_list/pick_list.json | 3 ++- erpnext/stock/doctype/pick_list/pick_list.py | 7 +++++-- erpnext/stock/doctype/pick_list_item/pick_list_item.json | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index e635db0540..8d5ef3d12a 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -96,6 +96,7 @@ "options": "Material Request" }, { + "depends_on": "eval:doc.docstatus===0", "fieldname": "get_item_locations", "fieldtype": "Button", "label": "Get Item Locations" @@ -110,7 +111,7 @@ } ], "is_submittable": 1, - "modified": "2019-08-28 12:49:59.800807", + "modified": "2019-08-29 21:10:11.572387", "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 efe6da8e15..128ad2504d 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -170,6 +170,7 @@ def get_available_item_locations_for_serialized_item(item_code, from_warehouses, return locations def get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty): + warehouse_condition = 'and warehouse in %(warehouses)s' if from_warehouses else '' batch_locations = frappe.db.sql(""" SELECT sle.`warehouse`, @@ -181,15 +182,17 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re sle.batch_no = batch.name and sle.`item_code`=%(item_code)s and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s + {warehouse_condition} GROUP BY `warehouse`, `batch_no`, `item_code` HAVING `qty` > 0 ORDER BY IFNULL(batch.`expiry_date`, '2200-01-01'), batch.`creation` - """, { + """.format(warehouse_condition=warehouse_condition), { #nosec 'item_code': item_code, - 'today': today() + 'today': today(), + 'warehouses': from_warehouses }, as_dict=1) total_qty_available = sum(location.get('qty') for location in batch_locations) 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 552764dc2f..9ecdecdaa0 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -111,7 +111,7 @@ "fieldname": "stock_qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Qty (Stock UOM)", + "label": "Stock Qty", "read_only": 1 }, { @@ -168,7 +168,7 @@ } ], "istable": 1, - "modified": "2019-08-28 15:56:14.507776", + "modified": "2019-08-29 17:17:17.668199", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", From 9641edc5626e63bda8be72ef6eb2a70a495c02a1 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 29 Aug 2019 22:23:13 +0530 Subject: [PATCH 69/79] fix: Set item locations before save --- erpnext/stock/doctype/pick_list/pick_list.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 128ad2504d..06bb247d4e 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -16,6 +16,9 @@ from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note a # TODO: Prioritize SO or WO group warehouse class PickList(Document): + def before_save(self): + self.set_item_locations() + def before_submit(self): for item in self.locations: if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'): @@ -56,7 +59,7 @@ class PickList(Document): self.append('locations', location) def aggregate_item_qty(self): - locations = self.locations + locations = self.get('locations') self.item_count_map = {} # aggregate qty for same item item_map = frappe._dict() From 88885184dc94d2429a0ab8974b0edc910096c657 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 30 Aug 2019 09:30:18 +0530 Subject: [PATCH 70/79] fix: Show separate button for pick list creation in work order --- .../doctype/work_order/work_order.js | 150 ++++++++++-------- 1 file changed, 84 insertions(+), 66 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index ce66c8fd15..ce7b4f9425 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -4,16 +4,17 @@ frappe.ui.form.on("Work Order", { setup: function(frm) { frm.custom_make_buttons = { - 'Stock Entry': 'Make Stock Entry', - } + 'Stock Entry': 'Start', + 'Pick List': 'Create Pick List', + }; // Set query for warehouses - frm.set_query("wip_warehouse", function(doc) { + frm.set_query("wip_warehouse", function() { return { filters: { 'company': frm.doc.company, } - } + }; }); frm.set_query("source_warehouse", function() { @@ -21,7 +22,7 @@ frappe.ui.form.on("Work Order", { filters: { 'company': frm.doc.company, } - } + }; }); frm.set_query("source_warehouse", "required_items", function() { @@ -29,7 +30,7 @@ frappe.ui.form.on("Work Order", { filters: { 'company': frm.doc.company, } - } + }; }); frm.set_query("sales_order", function() { @@ -37,7 +38,7 @@ frappe.ui.form.on("Work Order", { filters: { "status": ["not in", ["Closed", "On Hold"]] } - } + }; }); frm.set_query("fg_warehouse", function() { @@ -46,7 +47,7 @@ frappe.ui.form.on("Work Order", { 'company': frm.doc.company, 'is_group': 0 } - } + }; }); frm.set_query("scrap_warehouse", function() { @@ -55,17 +56,19 @@ frappe.ui.form.on("Work Order", { 'company': frm.doc.company, 'is_group': 0 } - } + }; }); // Set query for BOM frm.set_query("bom_no", function() { if (frm.doc.production_item) { - return{ + return { query: "erpnext.controllers.queries.bom", filters: {item: cstr(frm.doc.production_item)} - } - } else msgprint(__("Please enter Production Item first")); + }; + } else { + frappe.msgprint(__("Please enter Production Item first")); + } }); // Set query for FG Item @@ -76,7 +79,7 @@ frappe.ui.form.on("Work Order", { ['is_stock_item', '=',1], ['default_bom', '!=', ''] ] - } + }; }); // Set query for FG Item @@ -85,12 +88,12 @@ frappe.ui.form.on("Work Order", { filters:[ ['Project', 'status', 'not in', 'Completed, Cancelled'] ] - } + }; }); // formatter for work order operation frm.set_indicator_formatter('operation', - function(doc) { return (frm.doc.qty==doc.completed_qty) ? "green" : "orange" }); + function(doc) { return (frm.doc.qty==doc.completed_qty) ? "green" : "orange"; }); }, onload: function(frm) { @@ -133,7 +136,7 @@ frappe.ui.form.on("Work Order", { if(not_completed && not_completed.length) { frm.add_custom_button(__('Create Job Card'), () => { - frm.trigger("make_job_card") + frm.trigger("make_job_card"); }).addClass('btn-primary'); } } @@ -151,7 +154,7 @@ frappe.ui.form.on("Work Order", { condition: (d) => { if (d.allow_alternative_item) {return true;} } - }) + }); }); } } @@ -285,13 +288,13 @@ frappe.ui.form.on("Work Order", { if(!frm.doc.skip_transfer){ var pending_complete = frm.doc.material_transferred_for_manufacturing - frm.doc.produced_qty; if(pending_complete) { - var title = __('{0} items in progress', [pending_complete]); var width = ((pending_complete / frm.doc.qty * 100) - added_min); + title = __('{0} items in progress', [pending_complete]); bars.push({ 'title': title, 'width': (width > 100 ? "99.5" : width) + '%', 'progress_class': 'progress-bar-warning' - }) + }); message = message + '. ' + title; } } @@ -377,7 +380,7 @@ frappe.ui.form.on("Work Order", { filters: [ ["Sales Order","name", "in", r.message] ] - } + }; }); } }); @@ -401,10 +404,10 @@ frappe.ui.form.on("Work Order Item", { frappe.model.set_value(row.doctype, row.name, "available_qty_at_source_warehouse", r.message); } - }) + }); } } -}) +}); frappe.ui.form.on("Work Order Operation", { workstation: function(frm, cdt, cdn) { @@ -421,7 +424,7 @@ frappe.ui.form.on("Work Order Operation", { erpnext.work_order.calculate_cost(frm.doc); erpnext.work_order.calculate_total_cost(frm); } - }) + }); } }, time_in_mins: function(frm, cdt, cdn) { @@ -447,10 +450,13 @@ erpnext.work_order = { const show_start_btn = (frm.doc.skip_transfer || frm.doc.transfer_material_against == 'Job Card') ? 0 : 1; - if (show_start_btn){ + if (show_start_btn) { if ((flt(doc.material_transferred_for_manufacturing) < flt(doc.qty)) && frm.doc.status != 'Stopped') { frm.has_start_btn = true; + frm.add_custom_button(__('Create Pick List'), function() { + erpnext.work_order.create_pick_list(frm); + }); var start_btn = frm.add_custom_button(__('Start'), function() { erpnext.work_order.make_se(frm, 'Material Transfer for Manufacture'); }); @@ -519,8 +525,8 @@ erpnext.work_order = { calculate_total_cost: function(frm) { var variable_cost = frm.doc.actual_operating_cost ? - flt(frm.doc.actual_operating_cost) : flt(frm.doc.planned_operating_cost) - frm.set_value("total_operating_cost", (flt(frm.doc.additional_operating_cost) + variable_cost)) + flt(frm.doc.actual_operating_cost) : flt(frm.doc.planned_operating_cost); + frm.set_value("total_operating_cost", (flt(frm.doc.additional_operating_cost) + variable_cost)); }, set_default_warehouse: function(frm) { @@ -528,60 +534,72 @@ erpnext.work_order = { frappe.call({ method: "erpnext.manufacturing.doctype.work_order.work_order.get_default_warehouse", callback: function(r) { - if(!r.exe) { + if (!r.exe) { frm.set_value("wip_warehouse", r.message.wip_warehouse); - frm.set_value("fg_warehouse", r.message.fg_warehouse) + frm.set_value("fg_warehouse", r.message.fg_warehouse); } } }); } }, - make_se: function(frm, purpose) { - if(!frm.doc.skip_transfer){ - var max = (purpose === "Manufacture") ? - flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty) : - flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing); + get_max_transferable_qty: (frm, purpose) => { + let max = 0; + if (frm.doc.skip_transfer) return max; + if (purpose === 'Manufacture') { + max = flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty); + } else { + max = flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing); } - max = flt(max, precision('qty')); + return flt(max, precision('qty')); + }, - frappe.prompt([{ - fieldtype: 'Float', - label: __('Qty for {0}', [purpose]), - fieldname: 'qty', - description: __('Max: {0}',[max]), - default: max - }, { - fieldtype: 'Select', - label: __('Create'), - fieldname: 'create', - default: 'Stock Entry', - options: 'Stock Entry\nPick List' - }], function (data) { - if (data.qty > max) { - frappe.msgprint(__('Quantity must not be more than {0}', [max])); - return; - } - if (data.create === 'Stock Entry') { - frappe.xcall('erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry', { + show_prompt_for_qty_input: function(frm, purpose) { + let max = this.get_max_transferable_qty(frm, purpose); + return new Promise((resolve, reject) => { + frappe.prompt({ + fieldtype: 'Float', + label: __('Qty for {0}', [purpose]), + fieldname: 'qty', + description: __('Max: {0}', [max]), + default: max + }, data => { + if (data.qty > max) { + frappe.msgprint(__('Quantity must not be more than {0}', [max])); + reject(); + } + data.purpose = purpose; + resolve(data); + }, __('Select Quantity'), __('Create')); + }); + }, + + make_se: function(frm, purpose) { + this.show_prompt_for_qty_input(frm, purpose) + .then(data => { + return frappe.xcall('erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry', { 'work_order_id': frm.doc.name, 'purpose': purpose, 'qty': data.qty - }).then(stock_entry => { - frappe.model.sync(stock_entry); - frappe.set_route('Form', stock_entry.doctype, stock_entry.name); }); - } else { - frappe.xcall('erpnext.manufacturing.doctype.work_order.work_order.create_pick_list', { + }).then(stock_entry => { + frappe.model.sync(stock_entry); + frappe.set_route('Form', stock_entry.doctype, stock_entry.name); + }); + + }, + + create_pick_list: function(frm, purpose='Material Transfer for Manufacture') { + this.show_prompt_for_qty_input(frm, purpose) + .then(data => { + return frappe.xcall('erpnext.manufacturing.doctype.work_order.work_order.create_pick_list', { 'source_name': frm.doc.name, 'for_qty': data.qty - }).then(pick_list => { - frappe.model.sync(pick_list); - frappe.set_route('Form', pick_list.doctype, pick_list.name); }); - } - - }, __('Select Quantity'), __('Create')); + }).then(pick_list => { + frappe.model.sync(pick_list); + frappe.set_route('Form', pick_list.doctype, pick_list.name); + }); }, make_consumption_se: function(frm, backflush_raw_materials_based_on) { @@ -621,6 +639,6 @@ erpnext.work_order = { frm.reload_doc(); } } - }) + }); } -} +}; From effe53d4ae9a46d4a15f50d34f8aae863bf4dc5d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 30 Aug 2019 09:52:08 +0530 Subject: [PATCH 71/79] fix: Set default value for qty --- erpnext/stock/doctype/pick_list_item/pick_list_item.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 9ecdecdaa0..c7a35df51f 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -30,6 +30,7 @@ ], "fields": [ { + "default": "1", "fieldname": "qty", "fieldtype": "Float", "in_list_view": 1, @@ -168,7 +169,7 @@ } ], "istable": 1, - "modified": "2019-08-29 17:17:17.668199", + "modified": "2019-08-29 21:28:39.539007", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", From 5e84ce55ae954ef4e634129cc5cd9bbe61c13eda Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 30 Aug 2019 10:01:49 +0530 Subject: [PATCH 72/79] fix: Add is_stock_item filter for item in locations table --- erpnext/stock/doctype/pick_list/pick_list.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 4b8df0939e..badb3e7574 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -30,6 +30,13 @@ frappe.ui.form.on('Pick List', { } }; }); + frm.set_query('item_code', 'locations', () => { + return { + filters: { + is_stock_item: 1 + } + }; + }); }, get_item_locations: (frm) => { frm.call('set_item_locations'); From 0df38891a16bd5cdba083253e70c8be2ab4d7d1d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 30 Aug 2019 15:59:35 +0530 Subject: [PATCH 73/79] fix: Update pick list print format --- erpnext/stock/print_format/pick_list/pick_list.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/print_format/pick_list/pick_list.json b/erpnext/stock/print_format/pick_list/pick_list.json index 56c819fc0c..0e88d24c5a 100644 --- a/erpnext/stock/print_format/pick_list/pick_list.json +++ b/erpnext/stock/print_format/pick_list/pick_list.json @@ -7,10 +7,10 @@ "docstatus": 0, "doctype": "Print Format", "font": "Default", - "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"
\\t\\t\\t\\t

Pick List
{{ doc.name }}\\t\\t\\t\\t

\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"parent_warehouse\", \"label\": \"Parent Warehouse\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"item_name\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"warehouse\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"serial_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"batch_no\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"item_locations\", \"label\": \"Item Locations\"}]", + "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"
\\t\\t\\t\\t

Pick List
{{ doc.name }}\\t\\t\\t\\t

\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"customer\", \"label\": \"Customer\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"purpose\", \"label\": \"Purpose\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item_name\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"warehouse\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"stock_qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"serial_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"batch_no\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"locations\", \"label\": \"Item Locations\"}]", "idx": 0, "line_breaks": 1, - "modified": "2019-08-02 07:58:35.504361", + "modified": "2019-08-30 15:58:27.807219", "modified_by": "Administrator", "module": "Stock", "name": "Pick List", From 0eed0a9abe372fa844c641aa386a5a4efbfb2da6 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 30 Aug 2019 19:12:16 +0530 Subject: [PATCH 74/79] test: Temporarily comment out tests --- .../stock/doctype/pick_list/test_pick_list.py | 159 +++++++++--------- 1 file changed, 80 insertions(+), 79 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index 96b770d1de..940d40e6c0 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -3,97 +3,98 @@ # See license.txt from __future__ import unicode_literals -import frappe +# import frappe import unittest # test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch'] class TestPickList(unittest.TestCase): def test_pick_list_picks_warehouse_for_each_item(self): - pick_list = frappe.get_doc({ - 'doctype': 'Pick List', - 'company': '_Test Company', - 'customer': '_Test Customer', - 'items_based_on': 'Sales Order', - 'items': [{ - 'item_code': '_Test Item Home Desktop 100', - 'qty': 5, - 'stock_qty': 5, - 'conversion_factor': 1, - 'sales_order': '_T-Sales Order-1', - 'sales_item': '_T-Sales Order-1_item', - }] - }) - pick_list.set_item_locations() - - self.assertEqual(pick_list.locations[0].item_code, '_Test Item Home Desktop 100') - self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') - self.assertEqual(pick_list.locations[0].qty, 5) - - def test_pick_list_skips_out_of_stock_item(self): - pick_list = frappe.get_doc({ - 'doctype': 'Pick List', - 'company': '_Test Company', - 'customer': '_Test Customer', - 'items_based_on': 'Sales Order', - 'items': [{ - 'item_code': '_Test Item Warehouse Group Wise Reorder', - 'qty': 1000, - 'stock_qty': 1000, - 'conversion_factor': 1, - 'sales_order': '_T-Sales Order-1', - 'sales_item': '_T-Sales Order-1_item', - }] - }) - - pick_list.set_item_locations() - - self.assertEqual(pick_list.locations[0].item_code, '_Test Item Warehouse Group Wise Reorder') - self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') - self.assertEqual(pick_list.locations[0].qty, 30) - - - def test_pick_list_skips_items_in_expired_batch(self): pass +# pick_list = frappe.get_doc({ +# 'doctype': 'Pick List', +# 'company': '_Test Company', +# 'customer': '_Test Customer', +# 'items_based_on': 'Sales Order', +# 'items': [{ +# 'item_code': '_Test Item Home Desktop 100', +# 'qty': 5, +# 'stock_qty': 5, +# 'conversion_factor': 1, +# 'sales_order': '_T-Sales Order-1', +# 'sales_item': '_T-Sales Order-1_item', +# }] +# }) +# pick_list.set_item_locations() - def test_pick_list_shows_serial_no_for_serialized_item(self): +# self.assertEqual(pick_list.locations[0].item_code, '_Test Item Home Desktop 100') +# self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') +# self.assertEqual(pick_list.locations[0].qty, 5) - stock_reconciliation = frappe.get_doc({ - 'doctype': 'Stock Reconciliation', - 'company': '_Test Company', - 'items': [{ - 'item_code': '_Test Serialized Item', - 'warehouse': '_Test Warehouse - _TC', - 'qty': 5, - 'serial_no': '123450\n123451\n123452\n123453\n123454' - }] - }) + # def test_pick_list_skips_out_of_stock_item(self): + # pick_list = frappe.get_doc({ + # 'doctype': 'Pick List', + # 'company': '_Test Company', + # 'customer': '_Test Customer', + # 'items_based_on': 'Sales Order', + # 'items': [{ + # 'item_code': '_Test Item Warehouse Group Wise Reorder', + # 'qty': 1000, + # 'stock_qty': 1000, + # 'conversion_factor': 1, + # 'sales_order': '_T-Sales Order-1', + # 'sales_item': '_T-Sales Order-1_item', + # }] + # }) - stock_reconciliation.submit() + # pick_list.set_item_locations() - pick_list = frappe.get_doc({ - 'doctype': 'Pick List', - 'company': '_Test Company', - 'customer': '_Test Customer', - 'items_based_on': 'Sales Order', - 'items': [{ - 'item_code': '_Test Serialized Item', - 'qty': 1000, - 'stock_qty': 1000, - 'conversion_factor': 1, - 'sales_order': '_T-Sales Order-1', - 'sales_item': '_T-Sales Order-1_item', - }] - }) - - pick_list.set_item_locations() - self.assertEqual(pick_list.locations[0].item_code, '_Test Serialized Item') - self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') - self.assertEqual(pick_list.locations[0].qty, 5) - self.assertEqual(pick_list.locations[0].serial_no, '123450\n123451\n123452\n123453\n123454') + # self.assertEqual(pick_list.locations[0].item_code, '_Test Item Warehouse Group Wise Reorder') + # self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') + # self.assertEqual(pick_list.locations[0].qty, 30) - def test_pick_list_for_multiple_reference_doctypes(self): - pass + # def test_pick_list_skips_items_in_expired_batch(self): + # pass + + # def test_pick_list_shows_serial_no_for_serialized_item(self): + + # stock_reconciliation = frappe.get_doc({ + # 'doctype': 'Stock Reconciliation', + # 'company': '_Test Company', + # 'items': [{ + # 'item_code': '_Test Serialized Item', + # 'warehouse': '_Test Warehouse - _TC', + # 'qty': 5, + # 'serial_no': '123450\n123451\n123452\n123453\n123454' + # }] + # }) + + # stock_reconciliation.submit() + + # pick_list = frappe.get_doc({ + # 'doctype': 'Pick List', + # 'company': '_Test Company', + # 'customer': '_Test Customer', + # 'items_based_on': 'Sales Order', + # 'items': [{ + # 'item_code': '_Test Serialized Item', + # 'qty': 1000, + # 'stock_qty': 1000, + # 'conversion_factor': 1, + # 'sales_order': '_T-Sales Order-1', + # 'sales_item': '_T-Sales Order-1_item', + # }] + # }) + + # pick_list.set_item_locations() + # self.assertEqual(pick_list.locations[0].item_code, '_Test Serialized Item') + # self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') + # self.assertEqual(pick_list.locations[0].qty, 5) + # self.assertEqual(pick_list.locations[0].serial_no, '123450\n123451\n123452\n123453\n123454') + + + # def test_pick_list_for_multiple_reference_doctypes(self): + # pass ## records required From 744b92d2339369af43ea5be9cf4fbc3844b911fe Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 30 Aug 2019 19:39:28 +0530 Subject: [PATCH 75/79] fix: Commonify item not available alert - Reorganise code - UX fixes --- erpnext/stock/doctype/pick_list/pick_list.js | 6 ++- erpnext/stock/doctype/pick_list/pick_list.py | 50 ++++++++++---------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index badb3e7574..7c1e39a7a8 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -39,7 +39,11 @@ frappe.ui.form.on('Pick List', { }); }, get_item_locations: (frm) => { - frm.call('set_item_locations'); + if (!frm.doc.locations || !frm.doc.locations.length) { + frappe.msgprint(__('First add items in the Item Locations table')); + } else { + frm.call('set_item_locations'); + } }, refresh: (frm) => { frm.trigger('add_get_items_button'); diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 06bb247d4e..51f41a9632 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -130,12 +130,24 @@ def get_items_with_location_and_quantity(item_doc, item_location_map): return locations def get_available_item_locations(item_code, from_warehouses, required_qty): + locations = [] if frappe.get_cached_value('Item', item_code, 'has_serial_no'): - return 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) elif frappe.get_cached_value('Item', item_code, 'has_batch_no'): - return 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) else: - return 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) + + total_qty_available = sum(location.get('qty') for location in locations) + + remaining_qty = required_qty - total_qty_available + + if remaining_qty > 0: + frappe.msgprint(_('{0} units of {1} is not available.') + .format(remaining_qty, frappe.get_desk_link('Item', item_code))) + + return locations + def get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty): filters = frappe._dict({ @@ -153,11 +165,6 @@ def get_available_item_locations_for_serialized_item(item_code, from_warehouses, order_by='purchase_date', as_list=1) - remaining_stock_qty = required_qty - len(serial_nos) - if remaining_stock_qty: - frappe.msgprint('{0} qty of {1} is not available.' - .format(remaining_stock_qty, item_code)) - warehouse_serial_nos_map = frappe._dict() for serial_no, warehouse in serial_nos: warehouse_serial_nos_map.setdefault(warehouse, []).append(serial_no) @@ -198,13 +205,6 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re 'warehouses': from_warehouses }, as_dict=1) - total_qty_available = sum(location.get('qty') for location in batch_locations) - - remaining_qty = required_qty - total_qty_available - - if remaining_qty > 0: - frappe.msgprint('No batches found for {} qty of {}.'.format(remaining_qty, item_code)) - return batch_locations def get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty): @@ -225,6 +225,7 @@ def get_available_item_locations_for_other_item(item_code, from_warehouses, requ return item_locations + @frappe.whitelist() def create_delivery_note(source_name, target_doc=None): pick_list = frappe.get_doc('Pick List', source_name) @@ -323,6 +324,15 @@ def target_document_exists(pick_list_name, purpose): return stock_entry_exists(pick_list_name) +@frappe.whitelist() +def get_item_details(item_code, uom=None): + details = frappe.db.get_value('Item', item_code, ['stock_uom', 'name'], as_dict=1) + details.uom = uom or details.stock_uom + if uom: + details.update(get_conversion_factor(item_code, uom)) + + return details + def update_delivery_note_item(source, target, delivery_note): cost_center = frappe.db.get_value('Project', delivery_note.project, 'cost_center') @@ -354,16 +364,6 @@ def stock_entry_exists(pick_list_name): 'pick_list': pick_list_name }) -@frappe.whitelist() -def get_item_details(item_code, uom=None): - details = frappe.db.get_value('Item', item_code, ['stock_uom', 'name'], as_dict=1) - details.uom = uom or details.stock_uom - if uom: - details.update(get_conversion_factor(item_code, uom)) - - return details - - def update_stock_entry_based_on_work_order(pick_list, stock_entry): work_order = frappe.get_doc("Work Order", pick_list.get('work_order')) From bcfec77e2c514ed2d3c78ae2d348e7e93f657410 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 30 Aug 2019 19:44:37 +0530 Subject: [PATCH 76/79] style: Use single quotes --- erpnext/stock/doctype/pick_list/pick_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 7c1e39a7a8..3f66743f07 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -86,7 +86,7 @@ frappe.ui.form.on('Pick List', { target: frm, source_name: frm.doc.work_order }); - }, __("Select Quantity"), __('Get Items')); + }, __('Select Quantity'), __('Get Items')); }); }, material_request: (frm) => { From ca58fde552506acd38477886b63738f2a980b67b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sat, 31 Aug 2019 16:58:51 +0530 Subject: [PATCH 77/79] test: Enable basic tests --- .../stock/doctype/pick_list/test_pick_list.py | 216 +++++++++++------- 1 file changed, 133 insertions(+), 83 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index 940d40e6c0..d32e345047 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -3,108 +3,158 @@ # See license.txt from __future__ import unicode_literals -# import frappe +import frappe import unittest -# test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch'] +test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch'] + +from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation \ + import EmptyStockReconciliationItemsError class TestPickList(unittest.TestCase): + def test_pick_list_picks_warehouse_for_each_item(self): - pass -# pick_list = frappe.get_doc({ -# 'doctype': 'Pick List', -# 'company': '_Test Company', -# 'customer': '_Test Customer', -# 'items_based_on': 'Sales Order', -# 'items': [{ -# 'item_code': '_Test Item Home Desktop 100', -# 'qty': 5, -# 'stock_qty': 5, -# 'conversion_factor': 1, -# 'sales_order': '_T-Sales Order-1', -# 'sales_item': '_T-Sales Order-1_item', -# }] -# }) -# pick_list.set_item_locations() + try: + frappe.get_doc({ + 'doctype': 'Stock Reconciliation', + 'company': '_Test Company', + 'purpose': 'Opening Stock', + 'expense_account': 'Temporary Opening - _TC', + 'items': [{ + 'item_code': '_Test Item Home Desktop 100', + 'warehouse': '_Test Warehouse - _TC', + 'valuation_rate': 100, + 'qty': 5 + }] + }).submit() + except EmptyStockReconciliationItemsError: + pass -# self.assertEqual(pick_list.locations[0].item_code, '_Test Item Home Desktop 100') -# self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') -# self.assertEqual(pick_list.locations[0].qty, 5) + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'customer': '_Test Customer', + 'items_based_on': 'Sales Order', + 'locations': [{ + 'item_code': '_Test Item Home Desktop 100', + 'qty': 5, + 'stock_qty': 5, + 'conversion_factor': 1, + 'sales_order': '_T-Sales Order-1', + 'sales_item': '_T-Sales Order-1_item', + }] + }) + pick_list.set_item_locations() - # def test_pick_list_skips_out_of_stock_item(self): - # pick_list = frappe.get_doc({ - # 'doctype': 'Pick List', - # 'company': '_Test Company', - # 'customer': '_Test Customer', - # 'items_based_on': 'Sales Order', - # 'items': [{ - # 'item_code': '_Test Item Warehouse Group Wise Reorder', - # 'qty': 1000, - # 'stock_qty': 1000, - # 'conversion_factor': 1, - # 'sales_order': '_T-Sales Order-1', - # 'sales_item': '_T-Sales Order-1_item', - # }] - # }) + self.assertEqual(pick_list.locations[0].item_code, '_Test Item Home Desktop 100') + self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_list.locations[0].qty, 5) - # pick_list.set_item_locations() + def test_pick_list_splits_row_according_to_warhouse_availability(self): + try: + frappe.get_doc({ + 'doctype': 'Stock Reconciliation', + 'company': '_Test Company', + 'purpose': 'Opening Stock', + 'expense_account': 'Temporary Opening - _TC', + 'items': [{ + 'item_code': '_Test Item Warehouse Group Wise Reorder', + 'warehouse': '_Test Warehouse Group-C1 - _TC', + 'valuation_rate': 100, + 'qty': 5 + }] + }).submit() + except EmptyStockReconciliationItemsError: + pass - # self.assertEqual(pick_list.locations[0].item_code, '_Test Item Warehouse Group Wise Reorder') - # self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') - # self.assertEqual(pick_list.locations[0].qty, 30) + try: + frappe.get_doc({ + 'doctype': 'Stock Reconciliation', + 'company': '_Test Company', + 'purpose': 'Opening Stock', + 'expense_account': 'Temporary Opening - _TC', + 'items': [{ + 'item_code': '_Test Item Warehouse Group Wise Reorder', + 'warehouse': '_Test Warehouse 2 - _TC', + 'valuation_rate': 400, + 'qty': 10 + }] + }).submit() + except EmptyStockReconciliationItemsError: + pass + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'customer': '_Test Customer', + 'items_based_on': 'Sales Order', + 'locations': [{ + 'item_code': '_Test Item Warehouse Group Wise Reorder', + 'qty': 1000, + 'stock_qty': 1000, + 'conversion_factor': 1, + 'sales_order': '_T-Sales Order-1', + 'sales_item': '_T-Sales Order-1_item', + }] + }) - # def test_pick_list_skips_items_in_expired_batch(self): - # pass + pick_list.set_item_locations() - # def test_pick_list_shows_serial_no_for_serialized_item(self): + self.assertEqual(pick_list.locations[0].item_code, '_Test Item Warehouse Group Wise Reorder') + self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') + self.assertEqual(pick_list.locations[0].qty, 5) - # stock_reconciliation = frappe.get_doc({ - # 'doctype': 'Stock Reconciliation', - # 'company': '_Test Company', - # 'items': [{ - # 'item_code': '_Test Serialized Item', - # 'warehouse': '_Test Warehouse - _TC', - # 'qty': 5, - # 'serial_no': '123450\n123451\n123452\n123453\n123454' - # }] - # }) + self.assertEqual(pick_list.locations[1].item_code, '_Test Item Warehouse Group Wise Reorder') + self.assertEqual(pick_list.locations[1].warehouse, '_Test Warehouse 2 - _TC') + self.assertEqual(pick_list.locations[1].qty, 10) - # stock_reconciliation.submit() + def test_pick_list_shows_serial_no_for_serialized_item(self): - # pick_list = frappe.get_doc({ - # 'doctype': 'Pick List', - # 'company': '_Test Company', - # 'customer': '_Test Customer', - # 'items_based_on': 'Sales Order', - # 'items': [{ - # 'item_code': '_Test Serialized Item', - # 'qty': 1000, - # 'stock_qty': 1000, - # 'conversion_factor': 1, - # 'sales_order': '_T-Sales Order-1', - # 'sales_item': '_T-Sales Order-1_item', - # }] - # }) + stock_reconciliation = frappe.get_doc({ + 'doctype': 'Stock Reconciliation', + 'company': '_Test Company', + 'items': [{ + 'item_code': '_Test Serialized Item', + 'warehouse': '_Test Warehouse - _TC', + 'valuation_rate': 100, + 'qty': 5, + 'serial_no': '123450\n123451\n123452\n123453\n123454' + }] + }) - # pick_list.set_item_locations() - # self.assertEqual(pick_list.locations[0].item_code, '_Test Serialized Item') - # self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') - # self.assertEqual(pick_list.locations[0].qty, 5) - # self.assertEqual(pick_list.locations[0].serial_no, '123450\n123451\n123452\n123453\n123454') + stock_reconciliation.submit() + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'customer': '_Test Customer', + 'items_based_on': 'Sales Order', + 'locations': [{ + 'item_code': '_Test Serialized Item', + 'qty': 1000, + 'stock_qty': 1000, + 'conversion_factor': 1, + 'sales_order': '_T-Sales Order-1', + 'sales_item': '_T-Sales Order-1_item', + }] + }) + + pick_list.set_item_locations() + self.assertEqual(pick_list.locations[0].item_code, '_Test Serialized Item') + self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_list.locations[0].qty, 5) + self.assertEqual(pick_list.locations[0].serial_no, '123450\n123451\n123452\n123453\n123454') # def test_pick_list_for_multiple_reference_doctypes(self): # pass + # def test_pick_list_skips_items_in_expired_batch(self): + # pass -## records required + # def test_pick_list_from_sales_order(self): + # pass -''' -batch no -items -sales invoice -stock entries - bin - stock ledger entry -warehouses -''' \ No newline at end of file + # def test_pick_list_from_work_order(self): + # pass + + # def test_pick_list_from_material_request(self): + # pass \ No newline at end of file From 736b5e07cc59596a19dd9a5ac697ac4f6f1dfe10 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sat, 31 Aug 2019 18:33:23 +0530 Subject: [PATCH 78/79] fix: Maintain the order of Item List --- erpnext/stock/doctype/pick_list/pick_list.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 51f41a9632..c4d8c41ebb 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -8,6 +8,7 @@ import json from six import iteritems from frappe.model.document import Document from frappe import _ +from collections import OrderedDict from frappe.utils import floor, flt, today, cint from frappe.model.mapper import get_mapped_doc, map_child_doc from erpnext.stock.get_item_details import get_conversion_factor @@ -62,7 +63,7 @@ class PickList(Document): locations = self.get('locations') self.item_count_map = {} # aggregate qty for same item - item_map = frappe._dict() + item_map = OrderedDict() for item in locations: item_code = item.item_code reference = item.sales_order_item or item.material_request_item From 72bea0dff4d7552c20ee200a8d0ce248880589a8 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sat, 31 Aug 2019 18:34:25 +0530 Subject: [PATCH 79/79] test: Add test to check creation of pick list from multiple sales order --- .../stock/doctype/pick_list/test_pick_list.py | 70 +++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index d32e345047..6b4f73b140 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -40,7 +40,7 @@ class TestPickList(unittest.TestCase): 'stock_qty': 5, 'conversion_factor': 1, 'sales_order': '_T-Sales Order-1', - 'sales_item': '_T-Sales Order-1_item', + 'sales_order_item': '_T-Sales Order-1_item', }] }) pick_list.set_item_locations() @@ -93,7 +93,7 @@ class TestPickList(unittest.TestCase): 'stock_qty': 1000, 'conversion_factor': 1, 'sales_order': '_T-Sales Order-1', - 'sales_item': '_T-Sales Order-1_item', + 'sales_order_item': '_T-Sales Order-1_item', }] }) @@ -134,7 +134,7 @@ class TestPickList(unittest.TestCase): 'stock_qty': 1000, 'conversion_factor': 1, 'sales_order': '_T-Sales Order-1', - 'sales_item': '_T-Sales Order-1_item', + 'sales_order_item': '_T-Sales Order-1_item', }] }) @@ -144,8 +144,68 @@ class TestPickList(unittest.TestCase): self.assertEqual(pick_list.locations[0].qty, 5) self.assertEqual(pick_list.locations[0].serial_no, '123450\n123451\n123452\n123453\n123454') - # def test_pick_list_for_multiple_reference_doctypes(self): - # pass + def test_pick_list_for_items_from_multiple_sales_orders(self): + try: + frappe.get_doc({ + 'doctype': 'Stock Reconciliation', + 'company': '_Test Company', + 'purpose': 'Opening Stock', + 'expense_account': 'Temporary Opening - _TC', + 'items': [{ + 'item_code': '_Test Item Home Desktop 100', + 'warehouse': '_Test Warehouse - _TC', + 'valuation_rate': 100, + 'qty': 10 + }] + }).submit() + except EmptyStockReconciliationItemsError: + pass + + sales_order = frappe.get_doc({ + 'doctype': "Sales Order", + 'customer': '_Test Customer', + 'company': '_Test Company', + 'items': [{ + 'item_code': '_Test Item Home Desktop 100', + 'qty': 10, + 'delivery_date': frappe.utils.today() + }], + }) + sales_order.submit() + + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'customer': '_Test Customer', + 'items_based_on': 'Sales Order', + 'locations': [{ + 'item_code': '_Test Item Home Desktop 100', + 'qty': 5, + 'stock_qty': 5, + 'conversion_factor': 1, + 'sales_order': '_T-Sales Order-1', + 'sales_order_item': '_T-Sales Order-1_item', + }, { + 'item_code': '_Test Item Home Desktop 100', + 'qty': 5, + 'stock_qty': 5, + 'conversion_factor': 1, + 'sales_order': sales_order.name, + 'sales_order_item': sales_order.items[0].name, + }] + }) + pick_list.set_item_locations() + + self.assertEqual(pick_list.locations[0].item_code, '_Test Item Home Desktop 100') + self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_list.locations[0].qty, 5) + self.assertEqual(pick_list.locations[0].sales_order_item, '_T-Sales Order-1_item') + + self.assertEqual(pick_list.locations[1].item_code, '_Test Item Home Desktop 100') + self.assertEqual(pick_list.locations[1].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_list.locations[1].qty, 5) + self.assertEqual(pick_list.locations[1].sales_order_item, sales_order.items[0].name) + # def test_pick_list_skips_items_in_expired_batch(self): # pass