Date: Fri, 9 Aug 2019 17:50:03 +0530
Subject: [PATCH 027/122] fix: use primary label instead of bold letters
---
erpnext/public/js/templates/contact_list.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/erpnext/public/js/templates/contact_list.html b/erpnext/public/js/templates/contact_list.html
index a90580f652..0a339aa539 100644
--- a/erpnext/public/js/templates/contact_list.html
+++ b/erpnext/public/js/templates/contact_list.html
@@ -17,7 +17,7 @@
{% if (contact_list[i].phones || contact_list[i].email_ids) { %}
{% if(contact_list[i].phone) { %}
- {%= __("Phone ") %}: {%= contact_list[i].phone %}
+ {%= __("Phone ") %}: {%= contact_list[i].phone %} ({%= __("Primary") %})
{% endif %}
{% if(contact_list[i].phone_nos) { %}
{% for(var j=0, k=contact_list[i].phone_nos.length; j
{% if(contact_list[i].email_id) { %}
- {%= __("Email ") %}: {%= contact_list[i].email_id %}
+ {%= __("Email ") %}: {%= contact_list[i].email_id %} ({%= __("Primary") %})
{% endif %}
{% if(contact_list[i].email_ids) { %}
{% for(var j=0, k=contact_list[i].email_ids.length; j
Date: Fri, 16 Aug 2019 08:16:22 +0530
Subject: [PATCH 028/122] 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 029/122] 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 030/122] 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 031/122] 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 032/122] 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 033/122] 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 034/122] 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 9f4dc3fcd41a69bd3f0f11ce3172189b01a823e6 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Tue, 20 Aug 2019 22:20:02 +0530
Subject: [PATCH 035/122] fix: call popup get contact name from number
---
erpnext/crm/doctype/utils.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/erpnext/crm/doctype/utils.py b/erpnext/crm/doctype/utils.py
index 9cfab15995..2257ee5cce 100644
--- a/erpnext/crm/doctype/utils.py
+++ b/erpnext/crm/doctype/utils.py
@@ -1,6 +1,7 @@
import frappe
from frappe import _
import json
+from frappe.contacts.doctype.contact.contact import get_contact_with_phone_number
@frappe.whitelist()
def get_document_with_phone_number(number):
@@ -11,10 +12,10 @@ def get_document_with_phone_number(number):
'phone': ['like', '%{}'.format(number)],
'mobile_no': ['like', '%{}'.format(number)]
}
- contacts = frappe.get_all('Contact', or_filters=number_filter, limit=1)
+ contact = get_contact_with_phone_number(number)
- if contacts:
- return frappe.get_doc('Contact', contacts[0].name)
+ if contact:
+ return frappe.get_doc('Contact', contact)
leads = frappe.get_all('Lead', or_filters=number_filter, limit=1)
From d79a16c9f3c33faeb14ea35aefcf71db5f1f9ae9 Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Wed, 21 Aug 2019 09:03:26 +0530
Subject: [PATCH 036/122] 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 c6e02e2a74995f9eeb8dc96ff501223e22bf5f15 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Wed, 21 Aug 2019 13:36:41 +0530
Subject: [PATCH 037/122] fix: make contact structure call popup compatible
---
erpnext/communication/doctype/call_log/call_log.py | 6 +++++-
erpnext/crm/doctype/utils.py | 1 -
.../doctype/exotel_settings/exotel_settings.py | 1 -
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/erpnext/communication/doctype/call_log/call_log.py b/erpnext/communication/doctype/call_log/call_log.py
index c9fdfbe447..88965def45 100644
--- a/erpnext/communication/doctype/call_log/call_log.py
+++ b/erpnext/communication/doctype/call_log/call_log.py
@@ -70,9 +70,13 @@ def set_caller_information(doc, state):
numbers = [doc.get('phone'), doc.get('mobile_no')]
for_doc = doc.doctype.lower()
+ # Contact now has all the nos saved in child table
+ if doc.doctype == 'Contact':
+ numbers = [nos.phone for nos in doc.phone_nos]
+
for number in numbers:
if not number: continue
- print(number)
+
filters = frappe._dict({
'from': ['like', '%{}'.format(number.lstrip('0'))],
for_doc: ''
diff --git a/erpnext/crm/doctype/utils.py b/erpnext/crm/doctype/utils.py
index 756b0a2fc8..55532761c2 100644
--- a/erpnext/crm/doctype/utils.py
+++ b/erpnext/crm/doctype/utils.py
@@ -1,7 +1,6 @@
import frappe
from frappe import _
import json
-from frappe.contacts.doctype.contact.contact import get_contact_with_phone_number
@frappe.whitelist()
def get_last_interaction(contact=None, lead=None):
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
index 77de84ce5c..6a846efad7 100644
--- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
+++ b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
@@ -3,7 +3,6 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-# import frappe
from frappe.model.document import Document
import requests
import frappe
From 81f20891c14432f3c6a4c413d7d13d289e6b8914 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Wed, 21 Aug 2019 16:09:35 +0530
Subject: [PATCH 038/122] fix: query
---
erpnext/communication/doctype/call_log/call_log.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/communication/doctype/call_log/call_log.py b/erpnext/communication/doctype/call_log/call_log.py
index 88965def45..29342042ff 100644
--- a/erpnext/communication/doctype/call_log/call_log.py
+++ b/erpnext/communication/doctype/call_log/call_log.py
@@ -54,7 +54,7 @@ def get_employees_with_number(number):
if employee_emails: return employee_emails
employees = frappe.get_all('Employee', filters={
- 'cell_number': ['like', '%{}'.format(number.lstrip('0'))],
+ 'cell_number': ['like', '{}'.format(number.lstrip('0'))],
'user_id': ['!=', '']
}, fields=['user_id'])
From 298d38cde3c8a5d74ee9ddb61464f4911f70ee92 Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Wed, 21 Aug 2019 16:37:49 +0530
Subject: [PATCH 039/122] 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 040/122] 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 041/122] 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 042/122] 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 043/122] 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 210317c0b5e8cb50d7226fc9a796adfe9ec3be5e Mon Sep 17 00:00:00 2001
From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com>
Date: Fri, 23 Aug 2019 00:21:26 +0530
Subject: [PATCH 044/122] fix: division by zero in asset Depereciation (#18637)
* fix: division by zero in asset Depereciation
* fix: requested changes
---
erpnext/assets/doctype/asset/asset.py | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 45d2ec2c51..ab37e91538 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -255,9 +255,15 @@ class Asset(AccountsController):
precision = self.precision("gross_purchase_amount")
if row.depreciation_method in ("Straight Line", "Manual"):
+ depreciation_left = (cint(row.total_number_of_depreciations) - cint(self.number_of_depreciations_booked))
+
+ if not depreciation_left:
+ frappe.msgprint(_("All the depreciations has been booked"))
+ depreciation_amount = flt(row.expected_value_after_useful_life)
+ return depreciation_amount
+
depreciation_amount = (flt(row.value_after_depreciation) -
- flt(row.expected_value_after_useful_life)) / (cint(row.total_number_of_depreciations) -
- cint(self.number_of_depreciations_booked))
+ flt(row.expected_value_after_useful_life)) / depreciation_left
else:
depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100), precision)
@@ -275,7 +281,7 @@ class Asset(AccountsController):
flt(accumulated_depreciation_after_full_schedule),
self.precision('gross_purchase_amount'))
- if (row.expected_value_after_useful_life and
+ if (row.expected_value_after_useful_life and
row.expected_value_after_useful_life < asset_value_after_full_schedule):
frappe.throw(_("Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}")
.format(row.idx, asset_value_after_full_schedule))
From f2798eab502231ae5d0dce0bd26caa690baffc6b Mon Sep 17 00:00:00 2001
From: deepeshgarg007
Date: Fri, 23 Aug 2019 10:07:52 +0530
Subject: [PATCH 045/122] 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 88ee7d8dbeb62ff080247089219e2783d26c41d3 Mon Sep 17 00:00:00 2001
From: rohitwaghchaure
Date: Fri, 23 Aug 2019 11:13:58 +0530
Subject: [PATCH 046/122] fix: POS Sync Issue (#18807)
---
erpnext/accounts/doctype/sales_invoice/pos.py | 15 ++++-----------
erpnext/accounts/page/pos/pos.js | 11 ++---------
2 files changed, 6 insertions(+), 20 deletions(-)
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index 479548c6e3..e290b235a9 100755
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -432,7 +432,6 @@ def get_customer_id(doc, customer=None):
return cust_id
-
def make_customer_and_address(customers):
customers_list = []
for customer, data in iteritems(customers):
@@ -449,7 +448,6 @@ def make_customer_and_address(customers):
frappe.db.commit()
return customers_list
-
def add_customer(data):
customer = data.get('full_name') or data.get('customer')
if frappe.db.exists("Customer", customer.strip()):
@@ -466,21 +464,18 @@ def add_customer(data):
frappe.db.commit()
return customer_doc.name
-
def get_territory(data):
if data.get('territory'):
return data.get('territory')
return frappe.db.get_single_value('Selling Settings','territory') or _('All Territories')
-
def get_customer_group(data):
if data.get('customer_group'):
return data.get('customer_group')
return frappe.db.get_single_value('Selling Settings', 'customer_group') or frappe.db.get_value('Customer Group', {'is_group': 0}, 'name')
-
def make_contact(args, customer):
if args.get('email_id') or args.get('phone'):
name = frappe.db.get_value('Dynamic Link',
@@ -506,7 +501,6 @@ def make_contact(args, customer):
doc.flags.ignore_mandatory = True
doc.save(ignore_permissions=True)
-
def make_address(args, customer):
if not args.get('address_line1'):
return
@@ -521,7 +515,10 @@ def make_address(args, customer):
address = frappe.get_doc('Address', name)
else:
address = frappe.new_doc('Address')
- address.country = frappe.get_cached_value('Company', args.get('company'), 'country')
+ if args.get('company'):
+ address.country = frappe.get_cached_value('Company',
+ args.get('company'), 'country')
+
address.append('links', {
'link_doctype': 'Customer',
'link_name': customer
@@ -533,7 +530,6 @@ def make_address(args, customer):
address.flags.ignore_mandatory = True
address.save(ignore_permissions=True)
-
def make_email_queue(email_queue):
name_list = []
for key, data in iteritems(email_queue):
@@ -550,7 +546,6 @@ def make_email_queue(email_queue):
return name_list
-
def validate_item(doc):
for item in doc.get('items'):
if not frappe.db.exists('Item', item.get('item_code')):
@@ -569,7 +564,6 @@ def validate_item(doc):
item_doc.save(ignore_permissions=True)
frappe.db.commit()
-
def submit_invoice(si_doc, name, doc, name_list):
try:
si_doc.insert()
@@ -585,7 +579,6 @@ def submit_invoice(si_doc, name, doc, name_list):
return name_list
-
def save_invoice(doc, name, name_list):
try:
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):
diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js
index 3834c96bfb..b5a02d0e49 100755
--- a/erpnext/accounts/page/pos/pos.js
+++ b/erpnext/accounts/page/pos/pos.js
@@ -1762,18 +1762,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.si_docs = this.get_submitted_invoice() || [];
this.email_queue_list = this.get_email_queue() || {};
this.customers_list = this.get_customers_details() || {};
- if(this.customer_doc) {
- this.freeze = this.customer_doc.display
- }
-
- freeze_screen = this.freeze_screen || false;
-
- if ((this.si_docs.length || this.email_queue_list || this.customers_list) && !this.freeze) {
- this.freeze = true;
+ if (this.si_docs.length || this.email_queue_list || this.customers_list) {
frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice",
- freeze: freeze_screen,
+ freeze: true,
args: {
doc_list: me.si_docs,
email_queue_list: me.email_queue_list,
From db88476f8b48965565bb0c45dd9a040e3d76bac2 Mon Sep 17 00:00:00 2001
From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com>
Date: Fri, 23 Aug 2019 11:27:00 +0530
Subject: [PATCH 047/122] feat: refactor invoice_discounting (#18629)
* feat: refactor invoice_discountig
* Update invoice_discounting.py
* Update invoice_discounting.js
---
.../discounted_invoice.json | 13 ++++-
.../invoice_discounting.js | 53 +++++++++++++------
.../invoice_discounting.py | 13 ++++-
3 files changed, 59 insertions(+), 20 deletions(-)
diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json
index 8d7ed74eb9..04d6303774 100644
--- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json
+++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json
@@ -8,7 +8,8 @@
"customer",
"column_break_3",
"posting_date",
- "outstanding_amount"
+ "outstanding_amount",
+ "debit_to"
],
"fields": [
{
@@ -48,10 +49,18 @@
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
+ },
+ {
+ "fetch_from": "sales_invoice.debit_to",
+ "fieldname": "debit_to",
+ "fieldtype": "Link",
+ "label": "Debit to",
+ "options": "Account",
+ "read_only": 1
}
],
"istable": 1,
- "modified": "2019-05-30 19:27:29.436153",
+ "modified": "2019-08-07 15:13:55.808349",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Discounted Invoice",
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js
index 5563f031bb..f1f88a8d64 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js
@@ -13,41 +13,57 @@ frappe.ui.form.on('Invoice Discounting', {
};
});
- frm.events.filter_accounts("bank_account", frm, {"account_type": "Bank"});
- frm.events.filter_accounts("bank_charges_account", frm, {"root_type": "Expense"});
- frm.events.filter_accounts("short_term_loan", frm, {"root_type": "Liability"});
- frm.events.filter_accounts("accounts_receivable_credit", frm, {"account_type": "Receivable"});
- frm.events.filter_accounts("accounts_receivable_discounted", frm, {"account_type": "Receivable"});
- frm.events.filter_accounts("accounts_receivable_unpaid", frm, {"account_type": "Receivable"});
+
+ frm.events.filter_accounts("bank_account", frm, [["account_type", "=", "Bank"]]);
+ frm.events.filter_accounts("bank_charges_account", frm, [["root_type", "=", "Expense"]]);
+ frm.events.filter_accounts("short_term_loan", frm, [["root_type", "=", "Liability"]]);
+ frm.events.filter_accounts("accounts_receivable_discounted", frm, [["account_type", "=", "Receivable"]]);
+ frm.events.filter_accounts("accounts_receivable_credit", frm, [["account_type", "=", "Receivable"]]);
+ frm.events.filter_accounts("accounts_receivable_unpaid", frm, [["account_type", "=", "Receivable"]]);
},
filter_accounts: (fieldname, frm, addl_filters) => {
- let filters = {
- "company": frm.doc.company,
- "is_group": 0
- };
- if(addl_filters) Object.assign(filters, addl_filters);
+ let filters = [
+ ["company", "=", frm.doc.company],
+ ["is_group", "=", 0]
+ ];
+ if(addl_filters){
+ filters = $.merge(filters , addl_filters);
+ }
frm.set_query(fieldname, () => { return { "filters": filters }; });
},
+ refresh_filters: (frm) =>{
+ let invoice_accounts = Object.keys(frm.doc.invoices).map(function(key) {
+ return frm.doc.invoices[key].debit_to;
+ });
+ let filters = [
+ ["account_type", "=", "Receivable"],
+ ["name", "not in", invoice_accounts]
+ ];
+ frm.events.filter_accounts("accounts_receivable_credit", frm, filters);
+ frm.events.filter_accounts("accounts_receivable_discounted", frm, filters);
+ frm.events.filter_accounts("accounts_receivable_unpaid", frm, filters);
+ },
+
refresh: (frm) => {
frm.events.show_general_ledger(frm);
- if(frm.doc.docstatus === 0) {
+ if (frm.doc.docstatus === 0) {
frm.add_custom_button(__('Get Invoices'), function() {
frm.events.get_invoices(frm);
});
}
- if(frm.doc.docstatus === 1 && frm.doc.status !== "Settled") {
- if(frm.doc.status == "Sanctioned") {
+ if (frm.doc.docstatus === 1 && frm.doc.status !== "Settled") {
+ if (frm.doc.status == "Sanctioned") {
frm.add_custom_button(__('Disburse Loan'), function() {
frm.events.create_disbursement_entry(frm);
}).addClass("btn-primary");
}
- if(frm.doc.status == "Disbursed") {
+ if (frm.doc.status == "Disbursed") {
frm.add_custom_button(__('Close Loan'), function() {
frm.events.close_loan(frm);
}).addClass("btn-primary");
@@ -64,7 +80,7 @@ frappe.ui.form.on('Invoice Discounting', {
},
set_end_date: (frm) => {
- if(frm.doc.loan_start_date && frm.doc.loan_period) {
+ if (frm.doc.loan_start_date && frm.doc.loan_period) {
let end_date = frappe.datetime.add_days(frm.doc.loan_start_date, frm.doc.loan_period);
frm.set_value("loan_end_date", end_date);
}
@@ -132,6 +148,7 @@ frappe.ui.form.on('Invoice Discounting', {
frm.doc.invoices = frm.doc.invoices.filter(row => row.sales_invoice);
let row = frm.add_child("invoices");
$.extend(row, v);
+ frm.events.refresh_filters(frm);
});
refresh_field("invoices");
}
@@ -190,8 +207,10 @@ frappe.ui.form.on('Invoice Discounting', {
frappe.ui.form.on('Discounted Invoice', {
sales_invoice: (frm) => {
frm.events.calculate_total_amount(frm);
+ frm.events.refresh_filters(frm);
},
invoices_remove: (frm) => {
frm.events.calculate_total_amount(frm);
+ frm.events.refresh_filters(frm);
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
index 29475d5644..36c29113ea 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
@@ -12,6 +12,7 @@ from erpnext.accounts.general_ledger import make_gl_entries
class InvoiceDiscounting(AccountsController):
def validate(self):
self.validate_mandatory()
+ self.validate_invoices()
self.calculate_total_amount()
self.set_status()
self.set_end_date()
@@ -24,6 +25,15 @@ class InvoiceDiscounting(AccountsController):
if self.docstatus == 1 and not (self.loan_start_date and self.loan_period):
frappe.throw(_("Loan Start Date and Loan Period are mandatory to save the Invoice Discounting"))
+ def validate_invoices(self):
+ discounted_invoices = [record.sales_invoice for record in
+ frappe.get_all("Discounted Invoice",fields = ["sales_invoice"], filters= {"docstatus":1})]
+
+ for record in self.invoices:
+ if record.sales_invoice in discounted_invoices:
+ frappe.throw("Row({0}): {1} is already discounted in {2}"
+ .format(record.idx, frappe.bold(record.sales_invoice), frappe.bold(record.parent)))
+
def calculate_total_amount(self):
self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices])
@@ -212,7 +222,8 @@ def get_invoices(filters):
name as sales_invoice,
customer,
posting_date,
- outstanding_amount
+ outstanding_amount,
+ debit_to
from `tabSales Invoice` si
where
docstatus = 1
From 680e098b872f51c9c2b1e899b5548c492b8d7873 Mon Sep 17 00:00:00 2001
From: Michelle Alva <50285544+michellealva@users.noreply.github.com>
Date: Fri, 23 Aug 2019 11:32:43 +0530
Subject: [PATCH 048/122] fix(dash): Added Tax exemption category and sub
category in Employee Tax and Benefits (#18545)
section
From 56d03a456b44890602ddd00130107893f6f223e1 Mon Sep 17 00:00:00 2001
From: Harshit <46772424+harshit-30@users.noreply.github.com>
Date: Fri, 23 Aug 2019 11:34:25 +0530
Subject: [PATCH 049/122] fix: add contract to CRM module (#18817)
---
erpnext/config/crm.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/erpnext/config/crm.py b/erpnext/config/crm.py
index 70784f3d5f..eba6c7a02a 100644
--- a/erpnext/config/crm.py
+++ b/erpnext/config/crm.py
@@ -41,6 +41,11 @@ def get_data():
"name": "Lead Source",
"description": _("Track Leads by Lead Source.")
},
+ {
+ "type": "doctype",
+ "name": "Contract",
+ "description": _("Helps you keep tracks of Contracts based on Supplier, Customer and Employee"),
+ },
]
},
{
From 23b5f4ec99d0f22348ba3b8f1384c9f14f9660ee Mon Sep 17 00:00:00 2001
From: KanchanChauhan
Date: Fri, 23 Aug 2019 11:40:09 +0530
Subject: [PATCH 050/122] fix: Leave Application status filter (#18770)
When we click on Status Open in list view it throws an error.
After fix, it filters the results on Open status
---
erpnext/hr/doctype/leave_application/leave_application_list.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/erpnext/hr/doctype/leave_application/leave_application_list.js b/erpnext/hr/doctype/leave_application/leave_application_list.js
index f69b182737..cbb4b73227 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_list.js
+++ b/erpnext/hr/doctype/leave_application/leave_application_list.js
@@ -5,6 +5,8 @@ frappe.listview_settings['Leave Application'] = {
return [__("Approved"), "green", "status,=,Approved"];
} else if (doc.status === "Rejected") {
return [__("Rejected"), "red", "status,=,Rejected"];
+ } else {
+ return [__("Open"), "red", "status,=,Open"];
}
}
};
From f3d78dd29c4c98d303fbcfe7f7e1db447c466a82 Mon Sep 17 00:00:00 2001
From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
Date: Fri, 23 Aug 2019 11:42:35 +0530
Subject: [PATCH 051/122] fix: Duplicate items check in Sales Invoice (#18660)
* fix: Duplicate items check in sales Invoice
* fix: Commonified function for duplicate items check in selling controller
* fix: Set valuation rate and transaction date in child tabel
---
erpnext/controllers/selling_controller.py | 29 +++++++++++++++++++
.../doctype/sales_order/sales_order.py | 9 ------
.../doctype/delivery_note/delivery_note.py | 17 -----------
3 files changed, 29 insertions(+), 26 deletions(-)
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 2cbe596770..52d58dce62 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -45,6 +45,7 @@ class SellingController(StockController):
self.set_gross_profit()
set_default_income_account_for_item(self)
self.set_customer_address()
+ self.validate_for_duplicate_items()
def set_missing_values(self, for_validate=False):
@@ -381,6 +382,34 @@ class SellingController(StockController):
if self.get(address_field):
self.set(address_display_field, get_address_display(self.get(address_field)))
+ def validate_for_duplicate_items(self):
+ check_list, chk_dupl_itm = [], []
+ if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
+ return
+
+ for d in self.get('items'):
+ if self.doctype == "Sales Invoice":
+ e = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or '']
+ f = [d.item_code, d.description, d.sales_order or d.delivery_note]
+ 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 '']
+ f = [d.item_code, d.description]
+
+ if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
+ if e in check_list:
+ frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
+ else:
+ check_list.append(e)
+ else:
+ if f in chk_dupl_itm:
+ frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
+ else:
+ chk_dupl_itm.append(f)
+
+
def validate_items(self):
# validate items to see if they have is_sales_item enabled
from erpnext.controllers.buying_controller import validate_item_type
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 09dc9a9932..4342af5e19 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -72,9 +72,7 @@ class SalesOrder(SellingController):
frappe.msgprint(_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(so[0][0], self.po_no))
def validate_for_items(self):
- check_list = []
for d in self.get('items'):
- check_list.append(cstr(d.item_code))
# used for production plan
d.transaction_date = self.transaction_date
@@ -83,13 +81,6 @@ class SalesOrder(SellingController):
where item_code = %s and warehouse = %s", (d.item_code, d.warehouse))
d.projected_qty = tot_avail_qty and flt(tot_avail_qty[0][0]) or 0
- # check for same entry multiple times
- unique_chk_list = set(check_list)
- if len(unique_chk_list) != len(check_list) and \
- not cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
- frappe.msgprint(_("Same item has been entered multiple times"),
- title=_("Warning"), indicator='orange')
-
def product_bundle_has_stock_item(self, product_bundle):
"""Returns true if product bundle has stock item"""
ret = len(frappe.db.sql("""select i.name from tabItem i, `tabProduct Bundle Item` pbi
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 2de9b975da..f79d127984 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -166,24 +166,7 @@ class DeliveryNote(SellingController):
frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project))
def validate_for_items(self):
- check_list, chk_dupl_itm = [], []
- if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
- return
-
for d in self.get('items'):
- 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]
-
- if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
- if e in check_list:
- frappe.msgprint(_("Note: Item {0} entered multiple times").format(d.item_code))
- else:
- check_list.append(e)
- else:
- if f in chk_dupl_itm:
- frappe.msgprint(_("Note: Item {0} entered multiple times").format(d.item_code))
- else:
- chk_dupl_itm.append(f)
#Customer Provided parts will have zero valuation rate
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
d.allow_zero_valuation_rate = 1
From c890401fec89eb97a0656a1e18a297b78fe805de Mon Sep 17 00:00:00 2001
From: Michelle Alva <50285544+michellealva@users.noreply.github.com>
Date: Fri, 23 Aug 2019 11:50:48 +0530
Subject: [PATCH 052/122] fix: added payroll period in HR module (#18543)
From 8b4d604cfda01e1a67503d10ce7ce175fed51f8a Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Fri, 23 Aug 2019 11:57:16 +0530
Subject: [PATCH 053/122] 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 6e7b9b5cb794705c15e8aa1575e7be206ef1470b Mon Sep 17 00:00:00 2001
From: Nabin Hait
Date: Fri, 23 Aug 2019 12:27:05 +0530
Subject: [PATCH 054/122] Update sales_invoice.py
---
erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 4f80b78c88..95c5dd5cd4 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -206,9 +206,9 @@ class SalesInvoice(SellingController):
total_amount_in_payments = 0
for payment in self.payments:
total_amount_in_payments += payment.amount
-
- if total_amount_in_payments < self.rounded_total:
- frappe.throw(_("Total payments amount can't be greater than {}".format(-self.rounded_total)))
+ invoice_total = self.rounded_total or self.grand_total
+ if total_amount_in_payments < invoice_total:
+ frappe.throw(_("Total payments amount can't be greater than {}".format(-invoice_total)))
def validate_pos_paid_amount(self):
if len(self.payments) == 0 and self.is_pos:
@@ -1510,4 +1510,4 @@ def create_invoice_discounting(source_name, target_doc=None):
"outstanding_amount": invoice.outstanding_amount
})
- return invoice_discounting
\ No newline at end of file
+ return invoice_discounting
From 59432418c6fe6dccfbc440f26c2c946ac0fbda59 Mon Sep 17 00:00:00 2001
From: deepeshgarg007
Date: Fri, 23 Aug 2019 12:45:48 +0530
Subject: [PATCH 055/122] 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 a7ab51c5ba95ae387302fca2d7d222344eccbf5c Mon Sep 17 00:00:00 2001
From: Tufan Kaynak <31142607+toofun666@users.noreply.github.com>
Date: Fri, 23 Aug 2019 10:20:52 +0300
Subject: [PATCH 056/122] Update vehicle_expenses.py (#18768)
---
erpnext/hr/report/vehicle_expenses/vehicle_expenses.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
index e2989e29c0..e5622b7ae1 100644
--- a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
+++ b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
@@ -23,7 +23,8 @@ def get_columns():
_("Model") + ":data:50", _("Location") + ":data:100",
_("Log") + ":Link/Vehicle Log:100", _("Odometer") + ":Int:80",
_("Date") + ":Date:100", _("Fuel Qty") + ":Float:80",
- _("Fuel Price") + ":Float:100",_("Service Expense") + ":Float:100"
+ _("Fuel Price") + ":Float:100",_("Fuel Expense") + ":Float:100",
+ _("Service Expense") + ":Float:100"
]
return columns
@@ -32,7 +33,8 @@ def get_log_data(filters):
data = frappe.db.sql("""select
vhcl.license_plate as "License", vhcl.make as "Make", vhcl.model as "Model",
vhcl.location as "Location", log.name as "Log", log.odometer as "Odometer",
- log.date as "Date", log.fuel_qty as "Fuel Qty", log.price as "Fuel Price"
+ log.date as "Date", log.fuel_qty as "Fuel Qty", log.price as "Fuel Price",
+ log.fuel_qty * log.price as "Fuel Expense"
from
`tabVehicle` vhcl,`tabVehicle Log` log
where
@@ -58,7 +60,7 @@ def get_chart_data(data,period_list):
total_ser_exp=0
for row in data:
if row["Date"] <= period.to_date and row["Date"] >= period.from_date:
- total_fuel_exp+=flt(row["Fuel Price"])
+ total_fuel_exp+=flt(row["Fuel Expense"])
total_ser_exp+=flt(row["Service Expense"])
fueldata.append([period.key,total_fuel_exp])
servicedata.append([period.key,total_ser_exp])
@@ -84,4 +86,4 @@ def get_chart_data(data,period_list):
}
}
chart["type"] = "line"
- return chart
\ No newline at end of file
+ return chart
From d94a389dd3d66ad74d47300fa572a0179684056a Mon Sep 17 00:00:00 2001
From: Mangesh-Khairnar
Date: Fri, 23 Aug 2019 16:31:09 +0530
Subject: [PATCH 057/122] chore: remove unwanted code (#18645)
---
.../doctype/payment_order/regional/india.js | 29 ---
erpnext/patches.txt | 4 +-
.../make_custom_fields_for_bank_remittance.py | 12 --
.../remove_bank_remittance_custom_fields.py | 14 ++
erpnext/regional/india/bank_remittance.py | 190 ------------------
erpnext/regional/india/setup.py | 8 -
6 files changed, 16 insertions(+), 241 deletions(-)
delete mode 100644 erpnext/accounts/doctype/payment_order/regional/india.js
delete mode 100644 erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py
create mode 100644 erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py
delete mode 100644 erpnext/regional/india/bank_remittance.py
diff --git a/erpnext/accounts/doctype/payment_order/regional/india.js b/erpnext/accounts/doctype/payment_order/regional/india.js
deleted file mode 100644
index 66d0f60c61..0000000000
--- a/erpnext/accounts/doctype/payment_order/regional/india.js
+++ /dev/null
@@ -1,29 +0,0 @@
-frappe.ui.form.on('Payment Order', {
- refresh: function(frm) {
- if (frm.doc.docstatus==1 && frm.doc.payment_order_type==='Payment Entry') {
- frm.add_custom_button(__('Generate Text File'), function() {
- frm.trigger("generate_text_and_download_file");
- });
- }
- },
- generate_text_and_download_file: (frm) => {
- return frappe.call({
- method: "erpnext.regional.india.bank_remittance.generate_report",
- args: {
- name: frm.doc.name
- },
- freeze: true,
- callback: function(r) {
- {
- frm.reload_doc();
- const a = document.createElement('a');
- let file_obj = r.message;
- a.href = file_obj.file_url;
- a.target = '_blank';
- a.download = file_obj.file_name;
- a.click();
- }
- }
- });
- }
-});
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 7b8280975d..dc6c6daa13 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -605,7 +605,6 @@ erpnext.patches.v11_1.rename_depends_on_lwp
execute:frappe.delete_doc("Report", "Inactive Items")
erpnext.patches.v11_1.delete_scheduling_tool
erpnext.patches.v12_0.rename_tolerance_fields
-erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019
execute:frappe.delete_doc_if_exists("Page", "support-analytics")
erpnext.patches.v12_0.remove_patient_medical_record_page
erpnext.patches.v11_1.move_customer_lead_to_dynamic_column
@@ -626,4 +625,5 @@ 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
-erpnext.patches.v12_0.generate_leave_ledger_entries
\ No newline at end of file
+erpnext.patches.v12_0.remove_bank_remittance_custom_fields
+erpnext.patches.v12_0.generate_leave_ledger_entries
diff --git a/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py b/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py
deleted file mode 100644
index 3d4a9952c7..0000000000
--- a/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from erpnext.regional.india.setup import make_custom_fields
-
-def execute():
- frappe.reload_doc("accounts", "doctype", "tax_category")
- frappe.reload_doc("stock", "doctype", "item_manufacturer")
- company = frappe.get_all('Company', filters = {'country': 'India'})
- if not company:
- return
-
- make_custom_fields()
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py b/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py
new file mode 100644
index 0000000000..d1446b3227
--- /dev/null
+++ b/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py
@@ -0,0 +1,14 @@
+from __future__ import unicode_literals
+import frappe
+from erpnext.regional.india.setup import make_custom_fields
+
+def execute():
+ frappe.reload_doc("accounts", "doctype", "tax_category")
+ frappe.reload_doc("stock", "doctype", "item_manufacturer")
+ company = frappe.get_all('Company', filters = {'country': 'India'})
+ if not company:
+ return
+ if frappe.db.exists("Custom Field", "Company-bank_remittance_section"):
+ deprecated_fields = ['bank_remittance_section', 'client_code', 'remittance_column_break', 'product_code']
+ for i in range(len(deprecated_fields)):
+ frappe.delete_doc("Custom Field", 'Company-'+deprecated_fields[i])
\ No newline at end of file
diff --git a/erpnext/regional/india/bank_remittance.py b/erpnext/regional/india/bank_remittance.py
deleted file mode 100644
index 85c9564722..0000000000
--- a/erpnext/regional/india/bank_remittance.py
+++ /dev/null
@@ -1,190 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-from frappe.utils import cint,cstr, today
-from frappe import _
-import re
-import datetime
-from collections import OrderedDict
-
-def create_bank_remittance_txt(name):
- payment_order = frappe.get_cached_doc("Payment Order", name)
-
- no_of_records = len(payment_order.get("references"))
- total_amount = sum(entry.get("amount") for entry in payment_order.get("references"))
-
- product_code, client_code, company_email = frappe.db.get_value("Company",
- filters={'name' : payment_order.company},
- fieldname=['product_code', 'client_code', 'email'])
-
- header, file_name = get_header_row(payment_order, client_code)
- batch = get_batch_row(payment_order, no_of_records, total_amount, product_code)
-
- detail = []
- for ref_doc in payment_order.get("references"):
- detail += get_detail_row(ref_doc, payment_order, company_email)
-
- trailer = get_trailer_row(no_of_records, total_amount)
- detail_records = "\n".join(detail)
-
- return "\n".join([header, batch, detail_records, trailer]), file_name
-
-@frappe.whitelist()
-def generate_report(name):
- data, file_name = create_bank_remittance_txt(name)
-
- f = frappe.get_doc({
- 'doctype': 'File',
- 'file_name': file_name,
- 'content': data,
- "attached_to_doctype": 'Payment Order',
- "attached_to_name": name,
- 'is_private': True
- })
- f.save()
- return {
- 'file_url': f.file_url,
- 'file_name': file_name
- }
-
-def generate_file_name(name, company_account, date):
- ''' generate file name with format (account_code)_mmdd_(payment_order_no) '''
- bank, acc_no = frappe.db.get_value("Bank Account", {"name": company_account}, ['bank', 'bank_account_no'])
- return bank[:1]+str(acc_no)[-4:]+'_'+date.strftime("%m%d")+sanitize_data(name, '')[4:]+'.txt'
-
-def get_header_row(doc, client_code):
- ''' Returns header row and generated file name '''
- file_name = generate_file_name(doc.name, doc.company_bank_account, doc.posting_date)
- header = ["H"]
- header.append(validate_field_size(client_code, "Client Code", 20))
- header += [''] * 3
- header.append(validate_field_size(file_name, "File Name", 20))
- return "~".join(header), file_name
-
-def get_batch_row(doc, no_of_records, total_amount, product_code):
- batch = ["B"]
- batch.append(validate_field_size(no_of_records, "No Of Records", 5))
- batch.append(validate_amount(format(total_amount, '0.2f'), 17))
- batch.append(sanitize_data(doc.name, '_')[:20])
- batch.append(format_date(doc.posting_date))
- batch.append(validate_field_size(product_code,"Product Code", 20))
- return "~".join(batch)
-
-def get_detail_row(ref_doc, payment_entry, company_email):
-
- payment_date = format_date(payment_entry.posting_date)
- payment_entry = frappe.get_cached_doc('Payment Entry', ref_doc.payment_entry)
- supplier_bank_details = frappe.get_cached_doc('Bank Account', ref_doc.bank_account)
- company_bank_acc_no = frappe.db.get_value("Bank Account", {'name': payment_entry.bank_account}, ['bank_account_no'])
-
- addr_link = frappe.db.get_value('Dynamic Link',
- {
- 'link_doctype': 'Supplier',
- 'link_name': 'Sample Supplier',
- 'parenttype':'Address',
- 'parent': ('like', '%-Billing')
- }, 'parent')
-
- supplier_billing_address = frappe.get_cached_doc('Address', addr_link)
- email = ','.join(filter(None, [supplier_billing_address.email_id, company_email]))
-
- detail = OrderedDict(
- record_identifier='D',
- payment_ref_no=sanitize_data(ref_doc.payment_entry),
- payment_type=cstr(payment_entry.mode_of_payment)[:10],
- amount=str(validate_amount(format(ref_doc.amount, '.2f'),13)),
- payment_date=payment_date,
- instrument_date=payment_date,
- instrument_number='',
- dr_account_no_client=str(validate_field_size(company_bank_acc_no, "Company Bank Account", 20)),
- dr_description='',
- dr_ref_no='',
- cr_ref_no='',
- bank_code_indicator='M',
- beneficiary_code='',
- beneficiary_name=sanitize_data(validate_information(payment_entry, "party", 160), ' '),
- beneficiary_bank=sanitize_data(validate_information(supplier_bank_details, "bank", 10)),
- beneficiary_branch_code=cstr(validate_information(supplier_bank_details, "branch_code", 11)),
- beneficiary_acc_no=validate_information(supplier_bank_details, "bank_account_no", 20),
- location='',
- print_location='',
- beneficiary_address_1=validate_field_size(sanitize_data(cstr(supplier_billing_address.address_line1), ' '), " Beneficiary Address 1", 50),
- beneficiary_address_2=validate_field_size(sanitize_data(cstr(supplier_billing_address.address_line2), ' '), " Beneficiary Address 2", 50),
- beneficiary_address_3='',
- beneficiary_address_4='',
- beneficiary_address_5='',
- beneficiary_city=validate_field_size(cstr(supplier_billing_address.city), "Beneficiary City", 20),
- beneficiary_zipcode=validate_field_size(cstr(supplier_billing_address.pincode), "Pin Code", 6),
- beneficiary_state=validate_field_size(cstr(supplier_billing_address.state), "Beneficiary State", 20),
- beneficiary_email=cstr(email)[:255],
- beneficiary_mobile=validate_field_size(cstr(supplier_billing_address.phone), "Beneficiary Mobile", 10),
- payment_details_1='',
- payment_details_2='',
- payment_details_3='',
- payment_details_4='',
- delivery_mode=''
- )
- detail_record = ["~".join(list(detail.values()))]
-
- detail_record += get_advice_rows(payment_entry)
- return detail_record
-
-def get_advice_rows(payment_entry):
- ''' Returns multiple advice rows for a single detail entry '''
- payment_entry_date = payment_entry.posting_date.strftime("%b%y%d%m").upper()
- mode_of_payment = payment_entry.mode_of_payment
- advice_rows = []
- for record in payment_entry.references:
- advice = ['E']
- advice.append(cstr(mode_of_payment))
- advice.append(cstr(record.total_amount))
- advice.append('')
- advice.append(cstr(record.outstanding_amount))
- advice.append(record.reference_name)
- advice.append(format_date(record.due_date))
- advice.append(payment_entry_date)
- advice_rows.append("~".join(advice))
- return advice_rows
-
-def get_trailer_row(no_of_records, total_amount):
- ''' Returns trailer row '''
- trailer = ["T"]
- trailer.append(validate_field_size(no_of_records, "No of Records", 5))
- trailer.append(validate_amount(format(total_amount, "0.2f"), 17))
- return "~".join(trailer)
-
-def sanitize_data(val, replace_str=''):
- ''' Remove all the non-alphanumeric characters from string '''
- pattern = re.compile('[\W_]+')
- return pattern.sub(replace_str, val)
-
-def format_date(val):
- ''' Convert a datetime object to DD/MM/YYYY format '''
- return val.strftime("%d/%m/%Y")
-
-def validate_amount(val, max_int_size):
- ''' Validate amount to be within the allowed limits '''
- int_size = len(str(val).split('.')[0])
-
- if int_size > max_int_size:
- frappe.throw(_("Amount for a single transaction exceeds maximum allowed amount, create a separate payment order by splitting the transactions"))
-
- return val
-
-def validate_information(obj, attr, max_size):
- ''' Checks if the information is not set in the system and is within the size '''
- if hasattr(obj, attr):
- return validate_field_size(getattr(obj, attr), frappe.unscrub(attr), max_size)
-
- else:
- frappe.throw(_("{0} is mandatory for generating remittance payments, set the field and try again".format(frappe.unscrub(attr))))
-
-def validate_field_size(val, label, max_size):
- ''' check the size of the val '''
- if len(cstr(val)) > max_size:
- frappe.throw(_("{0} field is limited to size {1}".format(label, max_size)))
- return cstr(val)
\ No newline at end of file
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 40b98ed19a..756c17dc3b 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -407,14 +407,6 @@ def make_custom_fields(update=True):
fieldtype='Link', options='Salary Component', insert_after='basic_component'),
dict(fieldname='arrear_component', label='Arrear Component',
fieldtype='Link', options='Salary Component', insert_after='hra_component'),
- dict(fieldname='bank_remittance_section', label='Bank Remittance Settings',
- fieldtype='Section Break', collapsible=1, insert_after='arrear_component'),
- dict(fieldname='client_code', label='Client Code', fieldtype='Data',
- insert_after='bank_remittance_section'),
- dict(fieldname='remittance_column_break', fieldtype='Column Break',
- insert_after='client_code'),
- dict(fieldname='product_code', label='Product Code', fieldtype='Data',
- insert_after='remittance_column_break'),
],
'Employee Tax Exemption Declaration':[
dict(fieldname='hra_section', label='HRA Exemption',
From fe579c2efdfe13fae20ef33986d535646dc6c579 Mon Sep 17 00:00:00 2001
From: Mangesh-Khairnar
Date: Fri, 23 Aug 2019 16:31:20 +0530
Subject: [PATCH 058/122] fix: show a descriptive message on submission of
duplicate account (#18486)
* fix: show a descriptive message on submission of duplicate account
* Update account.py
---
erpnext/accounts/doctype/account/account.py | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index 0e57b3f198..1adc4c4d2f 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -128,7 +128,8 @@ class Account(NestedSet):
"account_currency": self.account_currency,
"parent_account": parent_acc_name_map[company]
})
- doc.save()
+ if not self.check_if_child_acc_exists(doc):
+ doc.save()
frappe.msgprint(_("Account {0} is added in the child company {1}")
.format(doc.name, company))
@@ -172,6 +173,24 @@ class Account(NestedSet):
if frappe.db.get_value("GL Entry", {"account": self.name}):
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
+ def check_if_child_acc_exists(self, doc):
+ ''' Checks if a account in parent company exists in the '''
+ info = frappe.db.get_value("Account", {
+ "account_name": doc.account_name,
+ "account_number": doc.account_number
+ }, ['company', 'account_currency', 'is_group', 'root_type', 'account_type', 'balance_must_be', 'account_name'], as_dict=1)
+
+ if not info:
+ return
+
+ doc = vars(doc)
+ dict_diff = [k for k in info if k in doc and info[k] != doc[k] and k != "company"]
+ if dict_diff:
+ frappe.throw(_("Account {0} already exists in child company {1}. The following fields have different values, they should be same:")
+ .format(info.account_name, info.company, ''.join(dict_diff)))
+ else:
+ return True
+
def convert_group_to_ledger(self):
if self.check_if_child_exists():
throw(_("Account with child nodes cannot be converted to ledger"))
From d8c262fd5c9611c65a33bafac57c2084f3dc8bbb Mon Sep 17 00:00:00 2001
From: Nabin Hait
Date: Fri, 23 Aug 2019 16:32:58 +0530
Subject: [PATCH 059/122] fix: Translations of strings (#18825)
---
erpnext/accounts/report/balance_sheet/balance_sheet.py | 6 +++---
.../payment_period_based_on_invoice_date.js | 4 ++--
.../payment_period_based_on_invoice_date.py | 10 +++++-----
.../profit_and_loss_statement.py | 6 +++---
erpnext/selling/page/point_of_sale/point_of_sale.js | 2 +-
5 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py
index 7d9acf26ed..97ce4f21e8 100644
--- a/erpnext/accounts/report/balance_sheet/balance_sheet.py
+++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py
@@ -135,11 +135,11 @@ def get_chart_data(filters, columns, asset, liability, equity):
datasets = []
if asset_data:
- datasets.append({'name':'Assets', 'values': asset_data})
+ datasets.append({'name': _('Assets'), 'values': asset_data})
if liability_data:
- datasets.append({'name':'Liabilities', 'values': liability_data})
+ datasets.append({'name': _('Liabilities'), 'values': liability_data})
if equity_data:
- datasets.append({'name':'Equity', 'values': equity_data})
+ datasets.append({'name': _('Equity'), 'values': equity_data})
chart = {
"data": {
diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js
index ca243944e9..2343eaa846 100644
--- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js
+++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js
@@ -27,8 +27,8 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = {
fieldname:"payment_type",
label: __("Payment Type"),
fieldtype: "Select",
- options: "Incoming\nOutgoing",
- default: "Incoming"
+ options: __("Incoming") + "\n" + __("Outgoing"),
+ default: __("Incoming")
},
{
"fieldname":"party_type",
diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
index 89e0113fc8..24b5d87b5b 100644
--- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
+++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
@@ -39,8 +39,8 @@ def execute(filters=None):
return columns, data
def validate_filters(filters):
- if (filters.get("payment_type") == "Incoming" and filters.get("party_type") == "Supplier") or \
- (filters.get("payment_type") == "Outgoing" and filters.get("party_type") == "Customer"):
+ if (filters.get("payment_type") == _("Incoming") and filters.get("party_type") == "Supplier") or \
+ (filters.get("payment_type") == _("Outgoing") and filters.get("party_type") == "Customer"):
frappe.throw(_("{0} payment entries can not be filtered by {1}")\
.format(filters.payment_type, filters.party_type))
@@ -51,7 +51,7 @@ def get_columns(filters):
_("Party Type") + "::100",
_("Party") + ":Dynamic Link/Party Type:140",
_("Posting Date") + ":Date:100",
- _("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"),
+ _("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == _("Outgoing") else ":Link/Sales Invoice:130"),
_("Invoice Posting Date") + ":Date:130",
_("Payment Due Date") + ":Date:130",
_("Debit") + ":Currency:120",
@@ -69,7 +69,7 @@ def get_conditions(filters):
conditions = []
if not filters.party_type:
- if filters.payment_type == "Outgoing":
+ if filters.payment_type == _("Outgoing"):
filters.party_type = "Supplier"
else:
filters.party_type = "Customer"
@@ -101,7 +101,7 @@ def get_entries(filters):
def get_invoice_posting_date_map(filters):
invoice_details = {}
- dt = "Sales Invoice" if filters.get("payment_type") == "Incoming" else "Purchase Invoice"
+ dt = "Sales Invoice" if filters.get("payment_type") == _("Incoming") else "Purchase Invoice"
for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1):
invoice_details[t.name] = t
diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
index ac11868cab..d500c8116e 100644
--- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
+++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
@@ -75,11 +75,11 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss):
datasets = []
if income_data:
- datasets.append({'name': 'Income', 'values': income_data})
+ datasets.append({'name': _('Income'), 'values': income_data})
if expense_data:
- datasets.append({'name': 'Expense', 'values': expense_data})
+ datasets.append({'name': _('Expense'), 'values': expense_data})
if net_profit:
- datasets.append({'name': 'Net Profit/Loss', 'values': net_profit})
+ datasets.append({'name': _('Net Profit/Loss'), 'values': net_profit})
chart = {
"data": {
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index d233b41787..d2c2d70dbe 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -4,7 +4,7 @@ frappe.provide('erpnext.pos');
frappe.pages['point-of-sale'].on_page_load = function(wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
- title: 'Point of Sale',
+ title: __('Point of Sale'),
single_column: true
});
From 92095709374ddfdb68c155bcf0f1107fd8527454 Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Mon, 26 Aug 2019 06:33:27 +0530
Subject: [PATCH 060/122] 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 061/122] 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 062/122] 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 063/122] 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 064/122] 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 065/122] 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 066/122] 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 067/122] 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 068/122] 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 5dd1b508026c69c0109ec5f09f4e62c7dcfde725 Mon Sep 17 00:00:00 2001
From: Mangesh-Khairnar
Date: Mon, 26 Aug 2019 10:27:25 +0530
Subject: [PATCH 069/122] fix: update show in website on disabling item
(#18831)
---
erpnext/stock/doctype/item/item.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 1568729344..518fe74889 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -124,6 +124,7 @@ class Item(WebsiteGenerator):
self.update_defaults_from_item_group()
self.validate_auto_reorder_enabled_in_stock_settings()
self.cant_change()
+ self.update_show_in_website()
if not self.get("__islocal"):
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
@@ -476,6 +477,10 @@ class Item(WebsiteGenerator):
[self.remove(d) for d in to_remove]
+ def update_show_in_website(self):
+ if self.disabled:
+ self.show_in_website = False
+
def update_template_tables(self):
template = frappe.get_doc("Item", self.variant_of)
From db3bb793e9e4d6dd9b1b536c57afda5693de5689 Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Mon, 26 Aug 2019 11:29:18 +0530
Subject: [PATCH 070/122] feat: added domain info on footer (#18839)
---
erpnext/templates/includes/footer/footer_powered.html | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html
index faf5e9278c..8d5523ff97 100644
--- a/erpnext/templates/includes/footer/footer_powered.html
+++ b/erpnext/templates/includes/footer/footer_powered.html
@@ -1 +1,3 @@
-Powered by ERPNext
+{% set domains = frappe.get_doc("Domain Settings").active_domains %}
+
+Powered by ERPNext - {{ domains[0].domain if domains else 'Open Source' }} ERP Software
From 7243e774c27477ffeb30c0c155af7332697eb3df Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Mon, 26 Aug 2019 12:49:20 +0530
Subject: [PATCH 071/122] fix: add city, state and country
---
erpnext/public/js/templates/contact_list.html | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/erpnext/public/js/templates/contact_list.html b/erpnext/public/js/templates/contact_list.html
index 0a339aa539..0df19bb996 100644
--- a/erpnext/public/js/templates/contact_list.html
+++ b/erpnext/public/js/templates/contact_list.html
@@ -37,7 +37,17 @@
{% endif %}
{% if (contact_list[i].address) { %}
- {%= __("Address ") %}: {%= contact_list[i].address %}
+ {%= __("Address ") %}: {%= contact_list[i].address %}
+ {% if (contact_list[i].city) { %}
+ , {%= contact_list[i].city %}
+ {% endif %}
+ {% if (contact_list[i].state) { %}
+ , {%= contact_list[i].state %}
+ {% endif %}
+ {% if (contact_list[i].country) { %}
+ , {%= contact_list[i].country %}
+ {% endif %}
+
{% endif %}
{% } %}
From 8ac2a2f0c4f1ac3fae0bc23b1e7abdd1558d2126 Mon Sep 17 00:00:00 2001
From: Faris Ansari
Date: Mon, 26 Aug 2019 13:00:17 +0530
Subject: [PATCH 072/122] fix: Set default Parent Item Group
---
erpnext/setup/doctype/item_group/item_group.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 33ab992568..760b20a476 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -26,6 +26,10 @@ class ItemGroup(NestedSet, WebsiteGenerator):
def validate(self):
super(ItemGroup, self).validate()
+
+ if not self.parent_item_group:
+ self.parent_item_group = 'All Item Groups'
+
self.make_route()
def on_update(self):
From d6be8989e4d13f45383d919c1d816a8da7c1bc3c Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Mon, 26 Aug 2019 22:11:49 +0530
Subject: [PATCH 073/122] 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 9057239c3c92228ba0b957e47b73cc9e01de7891 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Mon, 26 Aug 2019 22:40:54 +0530
Subject: [PATCH 074/122] fix: display address
---
erpnext/public/js/templates/contact_list.html | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/erpnext/public/js/templates/contact_list.html b/erpnext/public/js/templates/contact_list.html
index 0df19bb996..0a339aa539 100644
--- a/erpnext/public/js/templates/contact_list.html
+++ b/erpnext/public/js/templates/contact_list.html
@@ -37,17 +37,7 @@
{% endif %}
{% if (contact_list[i].address) { %}
- {%= __("Address ") %}: {%= contact_list[i].address %}
- {% if (contact_list[i].city) { %}
- , {%= contact_list[i].city %}
- {% endif %}
- {% if (contact_list[i].state) { %}
- , {%= contact_list[i].state %}
- {% endif %}
- {% if (contact_list[i].country) { %}
- , {%= contact_list[i].country %}
- {% endif %}
-
+ {%= __("Address ") %}: {%= contact_list[i].address %}
{% endif %}
{% } %}
From dc675b1eb0a0db97ed0ee6c1e8856d53bb70c58c Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Tue, 27 Aug 2019 08:06:22 +0530
Subject: [PATCH 075/122] 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 076/122] 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 077/122] 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 078/122] 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 079/122] 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 a5b78f3c0d3e8230b410f78eb2d576fcb4159434 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Wed, 28 Aug 2019 00:04:05 +0530
Subject: [PATCH 080/122] fix: get address in single line
---
erpnext/public/js/templates/contact_list.html | 2 ++
1 file changed, 2 insertions(+)
diff --git a/erpnext/public/js/templates/contact_list.html b/erpnext/public/js/templates/contact_list.html
index 0a339aa539..8dd220f72d 100644
--- a/erpnext/public/js/templates/contact_list.html
+++ b/erpnext/public/js/templates/contact_list.html
@@ -36,9 +36,11 @@
{% endif %}
{% endif %}
+
{% if (contact_list[i].address) { %}
{%= __("Address ") %}: {%= contact_list[i].address %}
{% endif %}
+
{% } %}
{% if(!contact_list.length) { %}
From 8f32ac0eddb0ded74828043bd3fdf5a39e538e5d Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Wed, 28 Aug 2019 10:16:20 +0530
Subject: [PATCH 081/122] 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 082/122] 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 083/122] 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 084/122] 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 085/122] 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 086/122] 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 087/122] 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 088/122] 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 089/122] 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 090/122] 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 091/122] 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 092/122] 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 093/122] 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 094/122] 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 095/122] 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 096/122] 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 097/122] 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 098/122] 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 099/122] 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 100/122] 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 101/122] 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 102/122] 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 103/122] 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 104/122] 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 105/122] 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 106/122] 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 107/122] 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
From fd85b1689d38a746387044597ce9fbf2f0571a90 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Mon, 2 Sep 2019 12:10:15 +0530
Subject: [PATCH 108/122] fix: review fixes
---
erpnext/communication/doctype/call_log/call_log.py | 2 +-
erpnext/crm/doctype/opportunity/test_opportunity.py | 6 +++---
erpnext/public/js/templates/contact_list.html | 6 +++---
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/erpnext/communication/doctype/call_log/call_log.py b/erpnext/communication/doctype/call_log/call_log.py
index 411f56c5ec..35c31a0bf8 100644
--- a/erpnext/communication/doctype/call_log/call_log.py
+++ b/erpnext/communication/doctype/call_log/call_log.py
@@ -75,7 +75,7 @@ def set_caller_information(doc, state):
# Contact now has all the nos saved in child table
if doc.doctype == 'Contact':
- numbers = [nos.phone for nos in doc.phone_nos]
+ numbers = [d.phone for d in doc.phone_nos]
for number in numbers:
number = strip_number(number)
diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py
index 8927d93027..8f61edf00e 100644
--- a/erpnext/crm/doctype/opportunity/test_opportunity.py
+++ b/erpnext/crm/doctype/opportunity/test_opportunity.py
@@ -45,7 +45,7 @@ class TestOpportunity(unittest.TestCase):
# create new customer and create new contact against 'new.opportunity@example.com'
customer = make_customer(opp_doc.party_name).insert(ignore_permissions=True)
- d = frappe.get_doc({
+ contact = frappe.get_doc({
"doctype": "Contact",
"first_name": "_Test Opportunity Customer",
"links": [{
@@ -53,8 +53,8 @@ class TestOpportunity(unittest.TestCase):
"link_name": customer.name
}]
})
- d.add_email(new_lead_email_id)
- d.insert(ignore_permissions=True)
+ contact.add_email(new_lead_email_id)
+ contact.insert(ignore_permissions=True)
opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)
self.assertTrue(opp_doc.party_name)
diff --git a/erpnext/public/js/templates/contact_list.html b/erpnext/public/js/templates/contact_list.html
index 8dd220f72d..da7b059fcd 100644
--- a/erpnext/public/js/templates/contact_list.html
+++ b/erpnext/public/js/templates/contact_list.html
@@ -21,7 +21,7 @@
{% endif %}
{% if(contact_list[i].phone_nos) { %}
{% for(var j=0, k=contact_list[i].phone_nos.length; j
+ {%= __("Phone") %}: {%= contact_list[i].phone_nos[j].phone %}
{% } %}
{% endif %}
@@ -31,14 +31,14 @@
{% endif %}
{% if(contact_list[i].email_ids) { %}
{% for(var j=0, k=contact_list[i].email_ids.length; j
+ {%= __("Email") %}: {%= contact_list[i].email_ids[j].email_id %}
{% } %}
{% endif %}
{% endif %}
{% if (contact_list[i].address) { %}
- {%= __("Address ") %}: {%= contact_list[i].address %}
+ {%= __("Address") %}: {%= contact_list[i].address %}
{% endif %}
From 26dfd5b3140d827c24a8f5ec061112809a243f21 Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Mon, 2 Sep 2019 12:21:41 +0530
Subject: [PATCH 109/122] fix: translation strings
---
erpnext/public/js/templates/contact_list.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/erpnext/public/js/templates/contact_list.html b/erpnext/public/js/templates/contact_list.html
index da7b059fcd..50fbfd9f12 100644
--- a/erpnext/public/js/templates/contact_list.html
+++ b/erpnext/public/js/templates/contact_list.html
@@ -17,7 +17,7 @@
{% if (contact_list[i].phones || contact_list[i].email_ids) { %}
{% if(contact_list[i].phone) { %}
- {%= __("Phone ") %}: {%= contact_list[i].phone %} ({%= __("Primary") %})
+ {%= __("Phone") %}: {%= contact_list[i].phone %} ({%= __("Primary") %})
{% endif %}
{% if(contact_list[i].phone_nos) { %}
{% for(var j=0, k=contact_list[i].phone_nos.length; j
{% if(contact_list[i].email_id) { %}
- {%= __("Email ") %}: {%= contact_list[i].email_id %} ({%= __("Primary") %})
+ {%= __("Email") %}: {%= contact_list[i].email_id %} ({%= __("Primary") %})
{% endif %}
{% if(contact_list[i].email_ids) { %}
{% for(var j=0, k=contact_list[i].email_ids.length; j
Date: Mon, 2 Sep 2019 13:20:27 +0530
Subject: [PATCH 110/122] fix: fix query for contacts
---
erpnext/accounts/doctype/sales_invoice/pos.py | 4 ++--
erpnext/selling/doctype/sms_center/sms_center.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index e290b235a9..7e23793700 100755
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -227,8 +227,8 @@ def get_contacts(customers):
customers = [frappe._dict({'name': customers})]
for data in customers:
- contact = frappe.db.sql(""" select email_id, phone, mobile_no from `tabContact`
- where is_primary_contact =1 and name in
+ contact = frappe.db.sql(""" select email_id, phone from `tabContact`
+ where is_primary_contact=1 and name in
(select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s
and parenttype = 'Contact')""", data.name, as_dict=1)
if contact:
diff --git a/erpnext/selling/doctype/sms_center/sms_center.py b/erpnext/selling/doctype/sms_center/sms_center.py
index bb6ba1ffce..289b045e1c 100644
--- a/erpnext/selling/doctype/sms_center/sms_center.py
+++ b/erpnext/selling/doctype/sms_center/sms_center.py
@@ -31,7 +31,7 @@ class SMSCenter(Document):
self.sales_partner.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''"
if self.send_to in ['All Contact', 'All Customer Contact', 'All Supplier Contact', 'All Sales Partner Contact']:
rec = frappe.db.sql("""select CONCAT(ifnull(c.first_name,''), ' ', ifnull(c.last_name,'')),
- c.mobile_no from `tabContact` c, `tabDynamic Link` dl where ifnull(c.mobile_no,'')!='' and
+ c.phone from `tabContact` c, `tabDynamic Link` dl where ifnull(c.phone,'')!='' and
c.docstatus != 2 and dl.parent = c.name%s""" % where_clause)
elif self.send_to == 'All Lead (Open)':
From 711d1acffd969e18859421e80c1677c6ab3b549f Mon Sep 17 00:00:00 2001
From: Himanshu Warekar
Date: Mon, 2 Sep 2019 13:31:10 +0530
Subject: [PATCH 111/122] fix: remove references of mobile_no
---
erpnext/accounts/page/pos/pos.js | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js
index b5a02d0e49..0e7301204a 100755
--- a/erpnext/accounts/page/pos/pos.js
+++ b/erpnext/accounts/page/pos/pos.js
@@ -816,7 +816,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
contact = me.contacts[data.name];
if(reg.test(data.name.toLowerCase())
|| reg.test(data.customer_name.toLowerCase())
- || (contact && reg.test(contact["mobile_no"]))
|| (contact && reg.test(contact["phone"]))
|| (data.customer_group && reg.test(data.customer_group.toLowerCase()))){
return data;
@@ -834,7 +833,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
if(contact && !c['phone']) {
c["phone"] = contact["phone"];
c["email_id"] = contact["email_id"];
- c["mobile_no"] = contact["mobile_no"];
}
me.customers_mapper.push({
@@ -844,10 +842,9 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
customer_group: c.customer_group,
territory: c.territory,
phone: contact ? contact["phone"] : '',
- mobile_no: contact ? contact["mobile_no"] : '',
email_id: contact ? contact["email_id"] : '',
searchtext: ['customer_name', 'customer_group', 'name', 'value',
- 'label', 'email_id', 'phone', 'mobile_no']
+ 'label', 'email_id', 'phone']
.map(key => c[key]).join(' ')
.toLowerCase()
});
From 402d82b4c54ad643832f1216384a220fe296766a Mon Sep 17 00:00:00 2001
From: deepeshgarg007
Date: Mon, 2 Sep 2019 14:37:18 +0530
Subject: [PATCH 112/122] fix: Remove delete_doc from tests
---
.../accounts/doctype/budget/test_budget.py | 52 +++++++++----------
.../loyalty_program/test_loyalty_program.py | 3 --
.../sales_invoice/test_sales_invoice.py | 1 -
.../test_tax_withholding_category.py | 3 --
erpnext/assets/doctype/asset/test_asset.py | 2 -
.../expense_claim/test_expense_claim.py | 1 -
.../test_stock_reconciliation.py | 4 --
7 files changed, 25 insertions(+), 41 deletions(-)
diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py
index b126b1fb0a..33aefd67d1 100644
--- a/erpnext/accounts/doctype/budget/test_budget.py
+++ b/erpnext/accounts/doctype/budget/test_budget.py
@@ -11,32 +11,32 @@ from erpnext.buying.doctype.purchase_order.test_purchase_order import create_pur
from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
-class TestBudget(unittest.TestCase):
+class TestBudget(unittest.TestCase):
def test_monthly_budget_crossed_ignore(self):
set_total_expense_zero("2013-02-28", "Cost Center")
budget = make_budget(budget_against="Cost Center")
-
+
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
-
+
budget.cancel()
def test_monthly_budget_crossed_stop1(self):
set_total_expense_zero("2013-02-28", "Cost Center")
budget = make_budget(budget_against="Cost Center")
-
+
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-02-28")
self.assertRaises(BudgetError, jv.submit)
-
+
budget.load_from_db()
budget.cancel()
@@ -46,7 +46,7 @@ class TestBudget(unittest.TestCase):
budget = make_budget(budget_against="Cost Center")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
-
+
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-03-02")
@@ -117,14 +117,14 @@ class TestBudget(unittest.TestCase):
set_total_expense_zero("2013-02-28", "Project")
budget = make_budget(budget_against="Project")
-
+
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project="_Test Project", posting_date="2013-02-28")
self.assertRaises(BudgetError, jv.submit)
-
+
budget.load_from_db()
budget.cancel()
@@ -132,31 +132,31 @@ class TestBudget(unittest.TestCase):
set_total_expense_zero("2013-02-28", "Cost Center")
budget = make_budget(budget_against="Cost Center")
-
+
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 150000, "_Test Cost Center - _TC", posting_date="2013-03-28")
self.assertRaises(BudgetError, jv.submit)
-
+
budget.cancel()
def test_yearly_budget_crossed_stop2(self):
set_total_expense_zero("2013-02-28", "Project")
budget = make_budget(budget_against="Project")
-
+
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 150000, "_Test Cost Center - _TC", project="_Test Project", posting_date="2013-03-28")
self.assertRaises(BudgetError, jv.submit)
-
+
budget.cancel()
def test_monthly_budget_on_cancellation1(self):
set_total_expense_zero("2013-02-28", "Cost Center")
budget = make_budget(budget_against="Cost Center")
-
+
jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
@@ -170,9 +170,9 @@ class TestBudget(unittest.TestCase):
{"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
-
+
self.assertRaises(BudgetError, jv1.cancel)
-
+
budget.load_from_db()
budget.cancel()
@@ -180,7 +180,7 @@ class TestBudget(unittest.TestCase):
set_total_expense_zero("2013-02-28", "Project")
budget = make_budget(budget_against="Project")
-
+
jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True, project="_Test Project")
@@ -194,16 +194,16 @@ class TestBudget(unittest.TestCase):
{"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
-
+
self.assertRaises(BudgetError, jv1.cancel)
-
+
budget.load_from_db()
budget.cancel()
def test_monthly_budget_against_group_cost_center(self):
set_total_expense_zero("2013-02-28", "Cost Center")
set_total_expense_zero("2013-02-28", "Cost Center", "_Test Cost Center 2 - _TC")
-
+
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
@@ -211,7 +211,7 @@ class TestBudget(unittest.TestCase):
"_Test Bank - _TC", 40000, "_Test Cost Center 2 - _TC", posting_date="2013-02-28")
self.assertRaises(BudgetError, jv.submit)
-
+
budget.load_from_db()
budget.cancel()
@@ -239,8 +239,6 @@ class TestBudget(unittest.TestCase):
budget.cancel()
jv.cancel()
- frappe.delete_doc('Journal Entry', jv.name)
- frappe.delete_doc('Cost Center', cost_center)
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
if budget_against_field == "Project":
@@ -256,7 +254,7 @@ def set_total_expense_zero(posting_date, budget_against_field=None, budget_again
"budget_against_field": budget_against_field,
"budget_against": budget_against
}))
-
+
if existing_expense:
if budget_against_field == "Cost Center":
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
@@ -281,13 +279,13 @@ def make_budget(**args):
frappe.db.sql("delete from `tabBudget Account` where parent = %(name)s", d)
budget = frappe.new_doc("Budget")
-
+
if budget_against == "Project":
budget.project = "_Test Project"
else:
budget.cost_center =cost_center or "_Test Cost Center - _TC"
-
-
+
+
budget.fiscal_year = "_Test Fiscal Year 2013"
budget.monthly_distribution = "_Test Distribution"
budget.company = "_Test Company"
@@ -299,7 +297,7 @@ def make_budget(**args):
"account": "_Test Account Cost for Goods Sold - _TC",
"budget_amount": 100000
})
-
+
if args.applicable_on_material_request:
budget.applicable_on_material_request = 1
budget.action_if_annual_budget_exceeded_on_mr = args.action_if_annual_budget_exceeded_on_mr or 'Warn'
diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
index 56a0d2f39c..4a7406e0cb 100644
--- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
+++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
@@ -49,7 +49,6 @@ class TestLoyaltyProgram(unittest.TestCase):
# cancel and delete
for d in [si_redeem, si_original]:
d.cancel()
- frappe.delete_doc('Sales Invoice', d.name)
def test_loyalty_points_earned_multiple_tier(self):
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty")
@@ -91,7 +90,6 @@ class TestLoyaltyProgram(unittest.TestCase):
# cancel and delete
for d in [si_redeem, si_original]:
d.cancel()
- frappe.delete_doc('Sales Invoice', d.name)
def test_cancel_sales_invoice(self):
''' cancelling the sales invoice should cancel the earned points'''
@@ -143,7 +141,6 @@ class TestLoyaltyProgram(unittest.TestCase):
d.cancel()
except frappe.TimestampMismatchError:
frappe.get_doc('Sales Invoice', d.name).cancel()
- frappe.delete_doc('Sales Invoice', d.name)
def test_loyalty_points_for_dashboard(self):
doc = frappe.get_doc('Customer', 'Test Loyalty Customer')
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index dff55947df..4f253b69f7 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -818,7 +818,6 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(expected_gl_entries[i][2], gle.credit)
si.cancel()
- frappe.delete_doc('Sales Invoice', si.name)
gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index 638e57ed2b..b1468999fc 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -48,7 +48,6 @@ class TestTaxWithholdingCategory(unittest.TestCase):
#delete invoices to avoid clashing
for d in invoices:
d.cancel()
- frappe.delete_doc("Purchase Invoice", d.name)
def test_single_threshold_tds(self):
invoices = []
@@ -83,7 +82,6 @@ class TestTaxWithholdingCategory(unittest.TestCase):
# delete invoices to avoid clashing
for d in invoices:
d.cancel()
- frappe.delete_doc("Purchase Invoice", d.name)
def test_single_threshold_tds_with_previous_vouchers(self):
invoices = []
@@ -102,7 +100,6 @@ class TestTaxWithholdingCategory(unittest.TestCase):
# delete invoices to avoid clashing
for d in invoices:
d.cancel()
- frappe.delete_doc("Purchase Invoice", d.name)
def create_purchase_invoice(**args):
# return sales invoice doc object
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 481ee7d9f4..c09b94fa8e 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -456,8 +456,6 @@ class TestAsset(unittest.TestCase):
self.assertEqual(gle, expected_gle)
si.cancel()
- frappe.delete_doc("Sales Invoice", si.name)
-
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
def test_asset_expected_value_after_useful_life(self):
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
index 6618a4f7c5..b559dfd81d 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
@@ -45,7 +45,6 @@ class TestExpenseClaim(unittest.TestCase):
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
expense_claim2.cancel()
- frappe.delete_doc("Expense Claim", expense_claim2.name)
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index ededc4d8b4..cd05929743 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -152,7 +152,6 @@ class TestStockReconciliation(unittest.TestCase):
for d in to_delete_records:
stock_doc = frappe.get_doc("Stock Reconciliation", d)
stock_doc.cancel()
- frappe.delete_doc("Stock Reconciliation", stock_doc.name)
for d in serial_nos + serial_nos1:
if frappe.db.exists("Serial No", d):
@@ -203,9 +202,6 @@ class TestStockReconciliation(unittest.TestCase):
stock_doc = frappe.get_doc("Stock Reconciliation", d)
stock_doc.cancel()
- frappe.delete_doc("Batch", sr.items[0].batch_no)
- for d in to_delete_records:
- frappe.delete_doc("Stock Reconciliation", d)
def insert_existing_sle():
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
From 063144c514ac4e42112935592380bd15466d6d7a Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Mon, 2 Sep 2019 15:53:28 +0530
Subject: [PATCH 113/122] fix: Check in_patch before adding 'All Item Group'
---
erpnext/setup/doctype/item_group/item_group.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 760b20a476..bb357d8bf8 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -27,7 +27,7 @@ class ItemGroup(NestedSet, WebsiteGenerator):
def validate(self):
super(ItemGroup, self).validate()
- if not self.parent_item_group:
+ if not self.parent_item_group and not frappe.flags.in_test:
self.parent_item_group = 'All Item Groups'
self.make_route()
From fffdb6f575a034403d230c3611ad4afdaa45853b Mon Sep 17 00:00:00 2001
From: Himanshu
Date: Mon, 2 Sep 2019 15:57:45 +0530
Subject: [PATCH 114/122] feat(Contacts): Multiple Emails in a Contact (#18675)
* feat: render multiple addresses
* feat: move to newer contacts structure
* fix: iterate over valid variable
* fix: use primary label instead of bold letters
* fix: call popup get contact name from number
* fix: make contact structure call popup compatible
* fix: query
* fix: add city, state and country
* fix: display address
* fix: get address in single line
* fix: review fixes
* fix: translation strings
* fix: fix query for contacts
* fix: remove references of mobile_no
---
erpnext/accounts/doctype/sales_invoice/pos.py | 4 +--
erpnext/accounts/page/pos/pos.js | 5 +--
.../doctype/call_log/call_log.py | 4 +++
.../doctype/opportunity/test_opportunity.py | 7 ++--
.../exotel_settings/exotel_settings.py | 1 -
erpnext/hub_node/legacy.py | 5 +--
erpnext/public/js/templates/contact_list.html | 35 +++++++++++++------
erpnext/selling/doctype/customer/customer.py | 7 ++--
.../selling/doctype/sms_center/sms_center.py | 2 +-
9 files changed, 43 insertions(+), 27 deletions(-)
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index e290b235a9..7e23793700 100755
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -227,8 +227,8 @@ def get_contacts(customers):
customers = [frappe._dict({'name': customers})]
for data in customers:
- contact = frappe.db.sql(""" select email_id, phone, mobile_no from `tabContact`
- where is_primary_contact =1 and name in
+ contact = frappe.db.sql(""" select email_id, phone from `tabContact`
+ where is_primary_contact=1 and name in
(select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s
and parenttype = 'Contact')""", data.name, as_dict=1)
if contact:
diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js
index b5a02d0e49..0e7301204a 100755
--- a/erpnext/accounts/page/pos/pos.js
+++ b/erpnext/accounts/page/pos/pos.js
@@ -816,7 +816,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
contact = me.contacts[data.name];
if(reg.test(data.name.toLowerCase())
|| reg.test(data.customer_name.toLowerCase())
- || (contact && reg.test(contact["mobile_no"]))
|| (contact && reg.test(contact["phone"]))
|| (data.customer_group && reg.test(data.customer_group.toLowerCase()))){
return data;
@@ -834,7 +833,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
if(contact && !c['phone']) {
c["phone"] = contact["phone"];
c["email_id"] = contact["email_id"];
- c["mobile_no"] = contact["mobile_no"];
}
me.customers_mapper.push({
@@ -844,10 +842,9 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
customer_group: c.customer_group,
territory: c.territory,
phone: contact ? contact["phone"] : '',
- mobile_no: contact ? contact["mobile_no"] : '',
email_id: contact ? contact["email_id"] : '',
searchtext: ['customer_name', 'customer_group', 'name', 'value',
- 'label', 'email_id', 'phone', 'mobile_no']
+ 'label', 'email_id', 'phone']
.map(key => c[key]).join(' ')
.toLowerCase()
});
diff --git a/erpnext/communication/doctype/call_log/call_log.py b/erpnext/communication/doctype/call_log/call_log.py
index 2343632719..35c31a0bf8 100644
--- a/erpnext/communication/doctype/call_log/call_log.py
+++ b/erpnext/communication/doctype/call_log/call_log.py
@@ -73,6 +73,10 @@ def set_caller_information(doc, state):
# contact_name or lead_name
display_name_field = '{}_name'.format(fieldname)
+ # Contact now has all the nos saved in child table
+ if doc.doctype == 'Contact':
+ numbers = [d.phone for d in doc.phone_nos]
+
for number in numbers:
number = strip_number(number)
if not number: continue
diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py
index 1a9f66ad9a..8f61edf00e 100644
--- a/erpnext/crm/doctype/opportunity/test_opportunity.py
+++ b/erpnext/crm/doctype/opportunity/test_opportunity.py
@@ -45,15 +45,16 @@ class TestOpportunity(unittest.TestCase):
# create new customer and create new contact against 'new.opportunity@example.com'
customer = make_customer(opp_doc.party_name).insert(ignore_permissions=True)
- frappe.get_doc({
+ contact = frappe.get_doc({
"doctype": "Contact",
- "email_id": new_lead_email_id,
"first_name": "_Test Opportunity Customer",
"links": [{
"link_doctype": "Customer",
"link_name": customer.name
}]
- }).insert(ignore_permissions=True)
+ })
+ contact.add_email(new_lead_email_id)
+ contact.insert(ignore_permissions=True)
opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)
self.assertTrue(opp_doc.party_name)
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
index 77de84ce5c..6a846efad7 100644
--- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
+++ b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
@@ -3,7 +3,6 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-# import frappe
from frappe.model.document import Document
import requests
import frappe
diff --git a/erpnext/hub_node/legacy.py b/erpnext/hub_node/legacy.py
index 95ada76a6a..85eb1b2bb0 100644
--- a/erpnext/hub_node/legacy.py
+++ b/erpnext/hub_node/legacy.py
@@ -68,12 +68,13 @@ def make_contact(supplier):
contact = frappe.get_doc({
'doctype': 'Contact',
'first_name': supplier.supplier_name,
- 'email_id': supplier.supplier_email,
'is_primary_contact': 1,
'links': [
{'link_doctype': 'Supplier', 'link_name': supplier.supplier_name}
]
- }).insert()
+ })
+ contact.add_email(supplier.supplier_email)
+ contact.insert()
else:
contact = frappe.get_doc('Contact', contact_name)
diff --git a/erpnext/public/js/templates/contact_list.html b/erpnext/public/js/templates/contact_list.html
index 893b4e0ec2..50fbfd9f12 100644
--- a/erpnext/public/js/templates/contact_list.html
+++ b/erpnext/public/js/templates/contact_list.html
@@ -14,20 +14,33 @@
style="margin-top:-3px; margin-right: -5px;">
{%= __("Edit") %}
- {% if (contact_list[i].phone || contact_list[i].mobile_no ||
- contact_list[i].email_id) { %}
+ {% if (contact_list[i].phones || contact_list[i].email_ids) { %}
- {% if(contact_list[i].phone) { %}
- {%= __("Phone") %}: {%= contact_list[i].phone %}
- {% } %}
- {% if(contact_list[i].mobile_no) { %}
- {%= __("Mobile No.") %}: {%= contact_list[i].mobile_no %}
- {% } %}
- {% if(contact_list[i].email_id) { %}
- {%= __("Email Address") %}: {%= contact_list[i].email_id %}
- {% } %}
+ {% if(contact_list[i].phone) { %}
+ {%= __("Phone") %}: {%= contact_list[i].phone %} ({%= __("Primary") %})
+ {% endif %}
+ {% if(contact_list[i].phone_nos) { %}
+ {% for(var j=0, k=contact_list[i].phone_nos.length; j
+ {% } %}
+ {% endif %}
+
+
+ {% if(contact_list[i].email_id) { %}
+ {%= __("Email") %}: {%= contact_list[i].email_id %} ({%= __("Primary") %})
+ {% endif %}
+ {% if(contact_list[i].email_ids) { %}
+ {% for(var j=0, k=contact_list[i].email_ids.length; j
+ {% } %}
+ {% endif %}
{% endif %}
+
+ {% if (contact_list[i].address) { %}
+ {%= __("Address") %}: {%= contact_list[i].address %}
+ {% endif %}
+
{% } %}
{% if(!contact_list.length) { %}
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index c946c47c59..d0b4ba0e65 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -337,14 +337,15 @@ def make_contact(args, is_primary_contact=1):
contact = frappe.get_doc({
'doctype': 'Contact',
'first_name': args.get('name'),
- 'mobile_no': args.get('mobile_no'),
- 'email_id': args.get('email_id'),
'is_primary_contact': is_primary_contact,
'links': [{
'link_doctype': args.get('doctype'),
'link_name': args.get('name')
}]
- }).insert()
+ })
+ contact.add_email(args.get('email_id'))
+ contact.add_phone(args.get('mobile_no'))
+ contact.insert()
return contact
diff --git a/erpnext/selling/doctype/sms_center/sms_center.py b/erpnext/selling/doctype/sms_center/sms_center.py
index bb6ba1ffce..289b045e1c 100644
--- a/erpnext/selling/doctype/sms_center/sms_center.py
+++ b/erpnext/selling/doctype/sms_center/sms_center.py
@@ -31,7 +31,7 @@ class SMSCenter(Document):
self.sales_partner.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''"
if self.send_to in ['All Contact', 'All Customer Contact', 'All Supplier Contact', 'All Sales Partner Contact']:
rec = frappe.db.sql("""select CONCAT(ifnull(c.first_name,''), ' ', ifnull(c.last_name,'')),
- c.mobile_no from `tabContact` c, `tabDynamic Link` dl where ifnull(c.mobile_no,'')!='' and
+ c.phone from `tabContact` c, `tabDynamic Link` dl where ifnull(c.phone,'')!='' and
c.docstatus != 2 and dl.parent = c.name%s""" % where_clause)
elif self.send_to == 'All Lead (Open)':
From e7c7a0e6487ce659428e5f1aefbc8c8af13a0b16 Mon Sep 17 00:00:00 2001
From: Mangesh-Khairnar
Date: Mon, 2 Sep 2019 15:59:44 +0530
Subject: [PATCH 115/122] fix: loan application (#18882)
* fix: loan application
* Update loan_application.py
---
erpnext/hr/doctype/loan_application/loan_application.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/erpnext/hr/doctype/loan_application/loan_application.py b/erpnext/hr/doctype/loan_application/loan_application.py
index 28d9c43f8e..582bf48bf0 100644
--- a/erpnext/hr/doctype/loan_application/loan_application.py
+++ b/erpnext/hr/doctype/loan_application/loan_application.py
@@ -30,7 +30,7 @@ class LoanApplication(Document):
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
if monthly_interest_rate:
min_repayment_amount = self.loan_amount*monthly_interest_rate
- if self.repayment_amount - min_repayment_amount <= 0:
+ if (self.repayment_amount - min_repayment_amount) <= 0:
frappe.throw(_("Repayment Amount must be greater than " \
+ str(flt(min_repayment_amount, 2))))
self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
@@ -58,10 +58,13 @@ def make_loan(source_name, target_doc = None):
doclist = get_mapped_doc("Loan Application", source_name, {
"Loan Application": {
"doctype": "Loan",
+ "field_map": {
+ "repayment_amount": "monthly_repayment_amount"
+ },
"validation": {
"docstatus": ["=", 1]
}
}
}, target_doc)
- return doclist
\ No newline at end of file
+ return doclist
From 8666ecc26f8bdba6346dddab782c44e7198ccfb3 Mon Sep 17 00:00:00 2001
From: deepeshgarg007
Date: Mon, 2 Sep 2019 17:41:12 +0530
Subject: [PATCH 116/122] fix: Purchase receipt test
---
.../stock/doctype/purchase_receipt/test_purchase_receipt.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index d124ae4747..ab9311b480 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -329,6 +329,11 @@ class TestPurchaseReceipt(unittest.TestCase):
location = frappe.db.get_value('Serial No', serial_nos[0].name, 'location')
self.assertEquals(location, "Test Location")
+ frappe.db.set_value("Asset", asset, "purchase_receipt", "")
+ frappe.db.set_value("Purchase Receipt Item", pr.items[0].name, "asset", "")
+
+ pr.load_from_db()
+
pr.cancel()
serial_nos = frappe.get_all('Serial No', {'asset': asset}, 'name') or []
self.assertEquals(len(serial_nos), 0)
From 7fa3f3fe98d6fc355cd51595116e0cad32e555ed Mon Sep 17 00:00:00 2001
From: Rohan
Date: Tue, 3 Sep 2019 14:16:12 +0530
Subject: [PATCH 117/122] fix: include start and end date for contract status
(#18866)
---
erpnext/crm/doctype/contract/contract.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/crm/doctype/contract/contract.py b/erpnext/crm/doctype/contract/contract.py
index 64cc97b503..18444cc7e6 100644
--- a/erpnext/crm/doctype/contract/contract.py
+++ b/erpnext/crm/doctype/contract/contract.py
@@ -88,7 +88,7 @@ def get_status(start_date, end_date):
end_date = getdate(end_date)
now_date = getdate(nowdate())
- return "Active" if start_date < now_date < end_date else "Inactive"
+ return "Active" if start_date <= now_date <= end_date else "Inactive"
def update_status_for_contracts():
From c174e4d01a1a6c6d2bd1d2353502d95ffccd5a37 Mon Sep 17 00:00:00 2001
From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
Date: Tue, 3 Sep 2019 14:17:41 +0530
Subject: [PATCH 118/122] fix: Default message from Payment Gateway Account not
fetching (#18848)
---
.../accounts/doctype/payment_request/payment_request.js | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js
index a455e74940..e2510f675f 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.js
+++ b/erpnext/accounts/doctype/payment_request/payment_request.js
@@ -1,7 +1,6 @@
-cur_frm.add_fetch("payment_gateway", "payment_account", "payment_account")
-cur_frm.add_fetch("payment_gateway", "payment_gateway", "payment_gateway")
-cur_frm.add_fetch("payment_gateway", "message", "message")
-cur_frm.add_fetch("payment_gateway", "payment_url_message", "payment_url_message")
+cur_frm.add_fetch("payment_gateway_account", "payment_account", "payment_account")
+cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway")
+cur_frm.add_fetch("payment_gateway_account", "message", "message")
frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){
if (frm.doc.reference_doctype) {
From cb44f3a7c345ac42ba1631c7ebe41f26e5474973 Mon Sep 17 00:00:00 2001
From: Mangesh-Khairnar
Date: Tue, 3 Sep 2019 14:18:15 +0530
Subject: [PATCH 119/122] fix(purchase-invoice): add rounded total property
setter for purchase invoice (#18841)
---
erpnext/setup/doctype/global_defaults/global_defaults.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.py b/erpnext/setup/doctype/global_defaults/global_defaults.py
index a39e246c68..fa7bc504b6 100644
--- a/erpnext/setup/doctype/global_defaults/global_defaults.py
+++ b/erpnext/setup/doctype/global_defaults/global_defaults.py
@@ -58,7 +58,7 @@ class GlobalDefaults(Document):
# Make property setters to hide rounded total fields
for doctype in ("Quotation", "Sales Order", "Sales Invoice", "Delivery Note",
- "Supplier Quotation", "Purchase Order"):
+ "Supplier Quotation", "Purchase Order", "Purchase Invoice"):
make_property_setter(doctype, "base_rounded_total", "hidden", self.disable_rounded_total, "Check")
make_property_setter(doctype, "base_rounded_total", "print_hide", 1, "Check")
From 1ab75db0acce6a4071dab999a39da0f851502e30 Mon Sep 17 00:00:00 2001
From: Nabin Hait
Date: Tue, 3 Sep 2019 16:07:46 +0530
Subject: [PATCH 120/122] refactor: Accounts Receivable / Payable report
(#18906)
---
.../accounts_receivable.html | 28 +-
.../accounts_receivable.js | 13 +-
.../accounts_receivable.py | 1228 ++++++++---------
.../test_accounts_receivable.py | 35 +-
.../accounts_receivable_summary.py | 345 ++---
erpnext/selling/doctype/customer/customer.py | 4 +-
6 files changed, 748 insertions(+), 905 deletions(-)
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html
index 192b6d7be3..d00bcf643e 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html
@@ -40,7 +40,7 @@
-{% if(filters.show_pdc_in_print) { %}
+{% if(filters.show_future_payments) { %}
{% var balance_row = data.slice(-1).pop();
var range1 = report.columns[11].label;
var range2 = report.columns[12].label;
@@ -122,22 +122,22 @@
{%= __("Date") %} |
{%= __("Age (Days)") %} |
- {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
+ {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
{%= __("Reference") %} |
{%= __("Sales Person") %} |
{% } else { %}
{%= __("Reference") %} |
{% } %}
- {% if(!filters.show_pdc_in_print) { %}
+ {% if(!filters.show_future_payments) { %}
{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %} |
{% } %}
{%= __("Invoiced Amount") %} |
- {% if(!filters.show_pdc_in_print) { %}
+ {% if(!filters.show_future_payments) { %}
{%= __("Paid Amount") %} |
{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %} |
{% } %}
{%= __("Outstanding Amount") %} |
- {% if(filters.show_pdc_in_print) { %}
+ {% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
{%= __("Customer LPO No.") %} |
{% } %}
@@ -162,18 +162,18 @@
{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %} |
{%= data[i][__("Age (Days)")] %} |
- {% if(!filters.show_pdc_in_print) { %}
+ {% if(!filters.show_future_payments) { %}
{%= data[i]["voucher_type"] %}
{% } %}
{%= data[i]["voucher_no"] %}
|
- {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
+ {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
{%= data[i]["sales_person"] %} |
{% } %}
- {% if(!filters.show_pdc_in_print) { %}
+ {% if(!filters.show_future_payments) { %}
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
@@ -195,7 +195,7 @@
|
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"]) %} |
- {% if(!filters.show_pdc_in_print) { %}
+ {% if(!filters.show_future_payments) { %}
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %} |
@@ -204,7 +204,7 @@
|
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %} |
- {% if(filters.show_pdc_in_print) { %}
+ {% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
{%= data[i]["po_no"] %} |
@@ -215,10 +215,10 @@
{% } %}
{% } else { %}
|
- {% if(!filters.show_pdc_in_print) { %}
+ {% if(!filters.show_future_payments) { %}
|
{% } %}
- {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
+ {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
|
{% } %}
|
@@ -226,7 +226,7 @@
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"] ) %} |
- {% if(!filters.show_pdc_in_print) { %}
+ {% if(!filters.show_future_payments) { %}
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %} |
{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %} |
@@ -234,7 +234,7 @@
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %} |
- {% if(filters.show_pdc_in_print) { %}
+ {% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
{%= data[i][__("Customer LPO")] %} |
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
index 4551973ac6..228be18d21 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
@@ -130,13 +130,18 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldtype": "Check",
},
{
- "fieldname":"show_pdc_in_print",
- "label": __("Show PDC in Print"),
+ "fieldname":"show_future_payments",
+ "label": __("Show Future Payments"),
"fieldtype": "Check",
},
{
- "fieldname":"show_sales_person_in_print",
- "label": __("Show Sales Person in Print"),
+ "fieldname":"show_delivery_notes",
+ "label": __("Show Delivery Notes"),
+ "fieldtype": "Check",
+ },
+ {
+ "fieldname":"show_sales_person",
+ "label": __("Show Sales Person"),
"fieldtype": "Check",
},
{
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 0e4ee12548..88e4227837 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -4,9 +4,33 @@
from __future__ import unicode_literals
import frappe, erpnext
from frappe import _, scrub
-from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr
+from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr, now, time_diff_in_seconds
+from collections import OrderedDict
+from erpnext.accounts.utils import get_currency_precision
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
+# This report gives a summary of all Outstanding Invoices considering the following
+
+# 1. Invoice can be booked via Sales/Purchase Invoice or Journal Entry
+# 2. Report handles both receivable and payable
+# 3. Key balances for each row are "Invoiced Amount", "Paid Amount", "Credit/Debit Note Amount", "Oustanding Amount"
+# 4. For explicit payment terms in invoice (example: 30% advance, 30% on delivery, 40% post delivery),
+# the invoice will be broken up into multiple rows, one for each payment term
+# 5. If there are payments after the report date (post dated), these will be updated in additional columns
+# for future amount
+# 6. Configurable Ageing Groups (0-30, 30-60 etc) can be set via filters
+# 7. For overpayment against an invoice with payment terms, there will be an additional row
+# 8. Invoice details like Sales Persons, Delivery Notes are also fetched comma separated
+# 9. Report amounts are in "Party Currency" if party is selected, or company currency for multi-party
+# 10. This reports is based on all GL Entries that are made against account_type "Receivable" or "Payable"
+
+def execute(filters=None):
+ args = {
+ "party_type": "Customer",
+ "naming_by": ["Selling Settings", "cust_master_name"],
+ }
+ return ReceivablePayableReport(filters).run(args)
+
class ReceivablePayableReport(object):
def __init__(self, filters=None):
self.filters = frappe._dict(filters or {})
@@ -16,459 +40,429 @@ class ReceivablePayableReport(object):
else self.filters.report_date
def run(self, args):
- party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
- columns = self.get_columns(party_naming_by, args)
- data = self.get_data(party_naming_by, args)
- chart = self.get_chart_data(columns, data)
- return columns, data, None, chart
-
- def get_columns(self, party_naming_by, args):
- columns = []
- columns.append({
- "label": _("Posting Date"),
- "fieldtype": "Date",
- "fieldname": "posting_date",
- "width": 90
- })
-
- columns += [_(args.get("party_type")) + ":Link/" + args.get("party_type") + ":200"]
-
- if party_naming_by == "Naming Series":
- columns += [args.get("party_type") + " Name::110"]
-
- if args.get("party_type") == 'Customer':
- columns.append({
- "label": _("Customer Contact"),
- "fieldtype": "Link",
- "fieldname": "contact",
- "options":"Contact",
- "width": 100
- })
-
- columns.append({
- "label": _("Voucher Type"),
- "fieldtype": "Data",
- "fieldname": "voucher_type",
- "width": 110
- })
-
- columns.append({
- "label": _("Voucher No"),
- "fieldtype": "Dynamic Link",
- "fieldname": "voucher_no",
- "width": 110,
- "options": "voucher_type",
- })
-
- columns += [_("Due Date") + ":Date:80"]
-
- if args.get("party_type") == "Supplier":
- columns += [_("Bill No") + "::80", _("Bill Date") + ":Date:80"]
-
- credit_or_debit_note = "Credit Note" if args.get("party_type") == "Customer" else "Debit Note"
-
- if self.filters.based_on_payment_terms:
- columns.append({
- "label": _("Payment Term"),
- "fieldname": "payment_term",
- "fieldtype": "Data",
- "width": 120
- })
- columns.append({
- "label": _("Invoice Grand Total"),
- "fieldname": "invoice_grand_total",
- "fieldtype": "Currency",
- "options": "currency",
- "width": 120
- })
-
- for label in ("Invoiced Amount", "Paid Amount", credit_or_debit_note, "Outstanding Amount"):
- columns.append({
- "label": _(label),
- "fieldname": frappe.scrub(label),
- "fieldtype": "Currency",
- "options": "currency",
- "width": 120
- })
-
- columns += [_("Age (Days)") + ":Int:80"]
-
- self.ageing_col_idx_start = len(columns)
-
- if not "range1" in self.filters:
- self.filters["range1"] = "30"
- if not "range2" in self.filters:
- self.filters["range2"] = "60"
- if not "range3" in self.filters:
- self.filters["range3"] = "90"
- if not "range4" in self.filters:
- self.filters["range4"] = "120"
-
- for label in ("0-{range1}".format(range1=self.filters["range1"]),
- "{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
- "{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
- "{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
- "{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))):
- columns.append({
- "label": label,
- "fieldname":label,
- "fieldtype": "Currency",
- "options": "currency",
- "width": 120
- })
-
- columns += [
- {
- "fieldname": "currency",
- "label": _("Currency"),
- "fieldtype": "Link",
- "options": "Currency",
- "width": 100
- },
- {
- "fieldname": "pdc/lc_ref",
- "label": _("PDC/LC Ref"),
- "fieldtype": "Data",
- "width": 110
- },
- {
- "fieldname": "pdc/lc_amount",
- "label": _("PDC/LC Amount"),
- "fieldtype": "Currency",
- "options": "currency",
- "width": 130
- },
- {
- "fieldname": "remaining_balance",
- "label": _("Remaining Balance"),
- "fieldtype": "Currency",
- "options": "currency",
- "width": 130
- }]
-
- if args.get('party_type') == 'Customer':
- columns += [
- {
- "label": _("Customer LPO"),
- "fieldtype": "Data",
- "fieldname": "po_no",
- "width": 100,
- },
- _("Delivery Note") + ":Data:100",
- _("Territory") + ":Link/Territory:80",
- _("Customer Group") + ":Link/Customer Group:120",
- {
- "label": _("Sales Person"),
- "fieldtype": "Data",
- "fieldname": "sales_person",
- "width": 120,
- }
- ]
- if args.get("party_type") == "Supplier":
- columns += [_("Supplier Group") + ":Link/Supplier Group:80"]
-
- columns.append(_("Remarks") + "::200")
-
- return columns
-
- def get_data(self, party_naming_by, args):
- from erpnext.accounts.utils import get_currency_precision
- self.currency_precision = get_currency_precision() or 2
- self.dr_or_cr = "debit" if args.get("party_type") == "Customer" else "credit"
-
- future_vouchers = self.get_entries_after(self.filters.report_date, args.get("party_type"))
+ self.filters.update(args)
+ self.set_defaults()
+ self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
+ self.get_columns()
+ self.get_data()
+ self.get_chart_data()
+ return self.columns, self.data, None, self.chart
+ def set_defaults(self):
if not self.filters.get("company"):
- self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company')
-
+ self.filters.company = frappe.db.get_single_value('Global Defaults', 'default_company')
self.company_currency = frappe.get_cached_value('Company', self.filters.get("company"), "default_currency")
+ self.currency_precision = get_currency_precision() or 2
+ self.dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit"
+ self.party_type = self.filters.party_type
+ self.party_details = {}
+ self.invoices = set()
- return_entries = self.get_return_entries(args.get("party_type"))
+ def get_data(self):
+ t1 = now()
+ self.get_gl_entries()
+ self.voucher_balance = OrderedDict()
+ self.init_voucher_balance() # invoiced, paid, credit_note, outstanding
- data = []
- self.pdc_details = get_pdc_details(args.get("party_type"), self.filters.report_date)
- gl_entries_data = self.get_entries_till(self.filters.report_date, args.get("party_type"))
+ # Build delivery note map against all sales invoices
+ self.build_delivery_note_map()
- if gl_entries_data:
- voucher_nos = [d.voucher_no for d in gl_entries_data] or []
- dn_details = get_dn_details(args.get("party_type"), voucher_nos)
- self.voucher_details = get_voucher_details(args.get("party_type"), voucher_nos, dn_details)
+ # Get invoice details like bill_no, due_date etc for all invoices
+ self.get_invoice_details()
- if self.filters.based_on_payment_terms and gl_entries_data:
- self.payment_term_map = self.get_payment_term_detail(voucher_nos)
+ # fetch future payments against invoices
+ self.get_future_payments()
- self.gle_inclusion_map = {}
- for gle in gl_entries_data:
- if self.is_receivable_or_payable(gle, self.dr_or_cr, future_vouchers, return_entries):
- self.gle_inclusion_map[gle.name] = True
- outstanding_amount, credit_note_amount, payment_amount = self.get_outstanding_amount(
- gle,self.filters.report_date, self.dr_or_cr, return_entries)
- temp_outstanding_amt = outstanding_amount
- temp_credit_note_amt = credit_note_amount
+ self.data = []
+ for gle in self.gl_entries:
+ self.update_voucher_balance(gle)
- if abs(outstanding_amount) > 0.1/10**self.currency_precision:
- if self.filters.based_on_payment_terms and self.payment_term_map.get(gle.voucher_no):
- for d in self.payment_term_map.get(gle.voucher_no):
- # Allocate payment amount based on payment terms(FIFO order)
- payment_amount, d.payment_amount = self.allocate_based_on_fifo(payment_amount, d.payment_term_amount)
+ self.build_data()
- term_outstanding_amount = d.payment_term_amount - d.payment_amount
+ def init_voucher_balance(self):
+ # build all keys, since we want to exclude vouchers beyond the report date
+ for gle in self.gl_entries:
+ # get the balance object for voucher_type
+ key = (gle.voucher_type, gle.voucher_no, gle.party)
+ if not key in self.voucher_balance:
+ self.voucher_balance[key] = frappe._dict(
+ voucher_type = gle.voucher_type,
+ voucher_no = gle.voucher_no,
+ party = gle.party,
+ posting_date = gle.posting_date,
+ remarks = gle.remarks,
+ invoiced = 0.0,
+ paid = 0.0,
+ credit_note = 0.0,
+ outstanding = 0.0
+ )
+ self.get_invoices(gle)
- # Allocate credit note based on payment terms(FIFO order)
- credit_note_amount, d.credit_note_amount = self.allocate_based_on_fifo(credit_note_amount, term_outstanding_amount)
+ def get_invoices(self, gle):
+ if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'):
+ self.invoices.add(gle.voucher_no)
- term_outstanding_amount -= d.credit_note_amount
-
- row_outstanding = term_outstanding_amount
- # Allocate PDC based on payment terms(FIFO order)
- d.pdc_details, d.pdc_amount = self.allocate_pdc_amount_in_fifo(gle, row_outstanding)
-
- if term_outstanding_amount > 0:
- row = self.prepare_row(party_naming_by, args, gle, term_outstanding_amount,
- d.credit_note_amount, d.due_date, d.payment_amount , d.payment_term_amount,
- d.description, d.pdc_amount, d.pdc_details)
- data.append(row)
-
- if credit_note_amount:
- row = self.prepare_row_without_payment_terms(party_naming_by, args, gle, temp_outstanding_amt,
- temp_credit_note_amt)
- data.append(row)
-
- else:
- row = self.prepare_row_without_payment_terms(party_naming_by, args, gle, outstanding_amount,
- credit_note_amount)
- data.append(row)
- return data
-
- def allocate_pdc_amount_in_fifo(self, gle, row_outstanding):
- pdc_list = self.pdc_details.get((gle.voucher_no, gle.party), [])
-
- pdc_details = []
- pdc_amount = 0
- for pdc in pdc_list:
- if row_outstanding <= pdc.pdc_amount:
- pdc_amount += row_outstanding
- pdc.pdc_amount -= row_outstanding
- if row_outstanding and pdc.pdc_ref and pdc.pdc_date:
- pdc_details.append(cstr(pdc.pdc_ref) + "/" + formatdate(pdc.pdc_date))
- row_outstanding = 0
+ def update_voucher_balance(self, gle):
+ # get the row where this balance needs to be updated
+ # if its a payment, it will return the linked invoice or will be considered as advance
+ row = self.get_voucher_balance(gle)
+ # gle_balance will be the total "debit - credit" for receivable type reports and
+ # and vice-versa for payable type reports
+ gle_balance = self.get_gle_balance(gle)
+ if gle_balance > 0:
+ if gle.voucher_type in ('Journal Entry', 'Payment Entry') and gle.against_voucher:
+ # debit against sales / purchase invoice
+ row.paid -= gle_balance
else:
- pdc_amount = pdc.pdc_amount
- if pdc.pdc_amount and pdc.pdc_ref and pdc.pdc_date:
- pdc_details.append(cstr(pdc.pdc_ref) + "/" + formatdate(pdc.pdc_date))
- pdc.pdc_amount = 0
- row_outstanding -= pdc_amount
-
- return pdc_details, pdc_amount
-
- def prepare_row_without_payment_terms(self, party_naming_by, args, gle, outstanding_amount, credit_note_amount):
- pdc_list = self.pdc_details.get((gle.voucher_no, gle.party), [])
- pdc_amount = 0
- pdc_details = []
- for d in pdc_list:
- pdc_amount += flt(d.pdc_amount)
- if pdc_amount and d.pdc_ref and d.pdc_date:
- pdc_details.append(cstr(d.pdc_ref) + "/" + formatdate(d.pdc_date))
-
- row = self.prepare_row(party_naming_by, args, gle, outstanding_amount,
- credit_note_amount, pdc_amount=pdc_amount, pdc_details=pdc_details)
-
- return row
-
-
- def allocate_based_on_fifo(self, total_amount, row_amount):
- allocated_amount = 0
- if row_amount <= total_amount:
- allocated_amount = row_amount
- total_amount -= row_amount
+ # invoice
+ row.invoiced += gle_balance
else:
- allocated_amount = total_amount
- total_amount = 0
+ # payment or credit note for receivables
+ if self.is_invoice(gle):
+ # stand alone debit / credit note
+ row.credit_note -= gle_balance
+ else:
+ # advance / unlinked payment or other adjustment
+ row.paid -= gle_balance
- return total_amount, allocated_amount
+ def get_voucher_balance(self, gle):
+ voucher_balance = None
- def prepare_row(self, party_naming_by, args, gle, outstanding_amount, credit_note_amount,
- due_date=None, paid_amt=None, payment_term_amount=None, payment_term=None, pdc_amount=None, pdc_details=None):
- row = [gle.posting_date, gle.party]
+ if gle.against_voucher:
+ # find invoice
+ voucher_balance = self.voucher_balance.get((gle.against_voucher_type, gle.against_voucher, gle.party))
- # customer / supplier name
- if party_naming_by == "Naming Series":
- row += [self.get_party_name(gle.party_type, gle.party)]
+ if not voucher_balance:
+ # no invoice, this is an invoice / stand-alone payment / credit note
+ voucher_balance = self.voucher_balance.get((gle.voucher_type, gle.voucher_no, gle.party))
- if args.get("party_type") == 'Customer':
- row += [self.get_customer_contact(gle.party_type, gle.party)]
+ return voucher_balance
- # get due date
- if not due_date:
- due_date = self.voucher_details.get(gle.voucher_no, {}).get("due_date", "")
- bill_date = self.voucher_details.get(gle.voucher_no, {}).get("bill_date", "")
+ def build_data(self):
+ # set outstanding for all the accumulated balances
+ # as we can use this to filter out invoices without outstanding
+ for key, row in self.voucher_balance.items():
+ row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
+ row.invoice_grand_total = row.invoiced
- row += [gle.voucher_type, gle.voucher_no, due_date]
+ if abs(row.outstanding) > 0.1/10 ** self.currency_precision:
+ # non-zero oustanding, we must consider this row
- # get supplier bill details
- if args.get("party_type") == "Supplier":
- row += [
- self.voucher_details.get(gle.voucher_no, {}).get("bill_no", ""),
- self.voucher_details.get(gle.voucher_no, {}).get("bill_date", "")
- ]
+ if self.is_invoice(row) and self.filters.based_on_payment_terms:
+ # is an invoice, allocate based on fifo
+ # adds a list `payment_terms` which contains new rows for each term
+ self.allocate_outstanding_based_on_payment_terms(row)
- # invoiced and paid amounts
- invoiced_amount = gle.get(self.dr_or_cr) if (gle.get(self.dr_or_cr) > 0) else 0
+ if row.payment_terms:
+ # make separate rows for each payment term
+ for d in row.payment_terms:
+ if d.outstanding > 0:
+ self.append_row(d)
- if self.filters.based_on_payment_terms:
- row+=[payment_term, invoiced_amount]
- if payment_term_amount:
- invoiced_amount = payment_term_amount
-
- if not payment_term_amount:
- paid_amt = invoiced_amount - outstanding_amount - credit_note_amount
- row += [invoiced_amount, paid_amt, credit_note_amount, outstanding_amount]
-
- # ageing data
- if self.filters.ageing_based_on == "Due Date":
- entry_date = due_date
- elif self.filters.ageing_based_on == "Supplier Invoice Date":
- entry_date = bill_date
- else:
- entry_date = gle.posting_date
-
- row += get_ageing_data(cint(self.filters.range1), cint(self.filters.range2),
- cint(self.filters.range3), cint(self.filters.range4), self.age_as_on, entry_date, outstanding_amount)
-
- # issue 6371-Ageing buckets should not have amounts if due date is not reached
- if self.filters.ageing_based_on == "Due Date" \
- and getdate(due_date) > getdate(self.filters.report_date):
- row[-1]=row[-2]=row[-3]=row[-4]=row[-5]=0
-
- if self.filters.ageing_based_on == "Supplier Invoice Date" \
- and getdate(bill_date) > getdate(self.filters.report_date):
-
- row[-1]=row[-2]=row[-3]=row[-4]=row[-5]=0
-
- if self.filters.get(scrub(args.get("party_type"))):
- row.append(gle.account_currency)
- else:
- row.append(self.company_currency)
-
- remaining_balance = outstanding_amount - flt(pdc_amount)
- pdc_details = ", ".join(pdc_details)
- row += [pdc_details, pdc_amount, remaining_balance]
-
- if args.get('party_type') == 'Customer':
- # customer LPO
- row += [self.voucher_details.get(gle.voucher_no, {}).get("po_no")]
-
- # Delivery Note
- row += [self.voucher_details.get(gle.voucher_no, {}).get("delivery_note")]
-
- # customer territory / supplier group
- if args.get("party_type") == "Customer":
- row += [self.get_territory(gle.party), self.get_customer_group(gle.party),
- self.voucher_details.get(gle.voucher_no, {}).get("sales_person")]
- if args.get("party_type") == "Supplier":
- row += [self.get_supplier_group(gle.party)]
-
- row.append(gle.remarks)
-
- return row
-
- def get_entries_after(self, report_date, party_type):
- # returns a distinct list
- return list(set([(e.voucher_type, e.voucher_no) for e in self.get_gl_entries(party_type, report_date, for_future=True)]))
-
- def get_entries_till(self, report_date, party_type):
- # returns a generator
- return self.get_gl_entries(party_type, report_date)
-
- def is_receivable_or_payable(self, gle, dr_or_cr, future_vouchers, return_entries):
- return (
- # advance
- (not gle.against_voucher) or
-
- # against sales order/purchase order
- (gle.against_voucher_type in ["Sales Order", "Purchase Order"]) or
-
- # sales invoice/purchase invoice
- (gle.against_voucher==gle.voucher_no and gle.get(dr_or_cr) > 0) or
-
- # standalone credit notes
- (gle.against_voucher==gle.voucher_no and gle.voucher_no in return_entries and not return_entries.get(gle.voucher_no)) or
-
- # entries adjusted with future vouchers
- ((gle.against_voucher_type, gle.against_voucher) in future_vouchers)
- )
-
- def get_return_entries(self, party_type):
- doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice"
- return_entries = frappe._dict(frappe.get_all(doctype,
- filters={"is_return": 1, "docstatus": 1}, fields=["name", "return_against"], as_list=1))
- return return_entries
-
- def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries):
- payment_amount, credit_note_amount = 0.0, 0.0
- reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit"
- for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
- if getdate(e.posting_date) <= report_date \
- and (e.name!=gle.name or (e.voucher_no in return_entries and not return_entries.get(e.voucher_no))):
- if e.name!=gle.name and self.gle_inclusion_map.get(e.name):
- continue
- self.gle_inclusion_map[e.name] = True
- amount = flt(e.get(reverse_dr_or_cr), self.currency_precision) - flt(e.get(dr_or_cr), self.currency_precision)
- if e.voucher_no not in return_entries:
- payment_amount += amount
+ # if there is overpayment, add another row
+ self.allocate_extra_payments_or_credits(row)
+ else:
+ self.append_row(row)
else:
- credit_note_amount += amount
+ self.append_row(row)
- voucher_amount = flt(gle.get(dr_or_cr), self.currency_precision) - flt(gle.get(reverse_dr_or_cr), self.currency_precision)
- if gle.voucher_no in return_entries and not return_entries.get(gle.voucher_no):
- voucher_amount = 0
+ def append_row(self, row):
+ self.allocate_future_payments(row)
+ self.set_invoice_details(row)
+ self.set_party_details(row)
+ self.set_ageing(row)
+ self.data.append(row)
- outstanding_amount = flt((voucher_amount - payment_amount - credit_note_amount), self.currency_precision)
- credit_note_amount = flt(credit_note_amount, self.currency_precision)
+ def set_invoice_details(self, row):
+ row.update(self.invoice_details.get(row.voucher_no, {}))
+ if row.voucher_type == 'Sales Invoice':
+ if self.filters.show_delivery_notes:
+ self.set_delivery_notes(row)
- return outstanding_amount, credit_note_amount, payment_amount
+ if self.filters.show_sales_person and row.sales_team:
+ row.sales_person = ", ".join(row.sales_team)
+ del row['sales_team']
- def get_party_name(self, party_type, party_name):
- return self.get_party_map(party_type).get(party_name, {}).get("customer_name" if party_type == "Customer" else "supplier_name") or ""
+ def set_delivery_notes(self, row):
+ delivery_notes = self.delivery_notes.get(row.voucher_no, [])
+ if delivery_notes:
+ row.delivery_notes = ', '.join(delivery_notes)
- def get_customer_contact(self, party_type, party_name):
- return self.get_party_map(party_type).get(party_name, {}).get("customer_primary_contact")
+ def build_delivery_note_map(self):
+ if self.invoices and self.filters.show_delivery_notes:
+ self.delivery_notes = frappe._dict()
- def get_territory(self, party_name):
- return self.get_party_map("Customer").get(party_name, {}).get("territory") or ""
+ # delivery note link inside sales invoice
+ si_against_dn = frappe.db.sql("""
+ select parent, delivery_note
+ from `tabSales Invoice Item`
+ where docstatus=1 and parent in (%s)
+ """ % (','.join(['%s'] * len(self.invoices))), tuple(self.invoices), as_dict=1)
- def get_customer_group(self, party_name):
- return self.get_party_map("Customer").get(party_name, {}).get("customer_group") or ""
+ for d in si_against_dn:
+ if d.delivery_note:
+ self.delivery_notes.setdefault(d.parent, set()).add(d.delivery_note)
- def get_supplier_group(self, party_name):
- return self.get_party_map("Supplier").get(party_name, {}).get("supplier_group") or ""
+ dn_against_si = frappe.db.sql("""
+ select distinct parent, against_sales_invoice
+ from `tabDelivery Note Item`
+ where against_sales_invoice in (%s)
+ """ % (','.join(['%s'] * len(self.invoices))), tuple(self.invoices) , as_dict=1)
- def get_party_map(self, party_type):
- if not hasattr(self, "party_map"):
- if party_type == "Customer":
- select_fields = "name, customer_name, territory, customer_group, customer_primary_contact"
- elif party_type == "Supplier":
- select_fields = "name, supplier_name, supplier_group"
+ for d in dn_against_si:
+ self.delivery_notes.setdefault(d.against_sales_invoice, set()).add(d.parent)
- self.party_map = dict(((r.name, r) for r in frappe.db.sql("select {0} from `tab{1}`"
- .format(select_fields, party_type), as_dict=True)))
+ def get_invoice_details(self):
+ self.invoice_details = frappe._dict()
+ if self.party_type == "Customer":
+ si_list = frappe.db.sql("""
+ select name, due_date, po_no
+ from `tabSales Invoice`
+ where posting_date <= %s
+ """,self.filters.report_date, as_dict=1)
+ for d in si_list:
+ self.invoice_details.setdefault(d.name, d)
- return self.party_map
+ # Get Sales Team
+ if self.filters.show_sales_person:
+ sales_team = frappe.db.sql("""
+ select parent, sales_person
+ from `tabSales Team`
+ where parenttype = 'Sales Invoice'
+ """, as_dict=1)
+ for d in sales_team:
+ self.invoice_details.setdefault(d.parent, {})\
+ .setdefault('sales_team', []).append(d.sales_person)
- def get_gl_entries(self, party_type, date=None, for_future=False):
- conditions, values = self.prepare_conditions(party_type)
+ if self.party_type == "Supplier":
+ for pi in frappe.db.sql("""
+ select name, due_date, bill_no, bill_date
+ from `tabPurchase Invoice`
+ where posting_date <= %s
+ """, self.filters.report_date, as_dict=1):
+ self.invoice_details.setdefault(pi.name, pi)
- if self.filters.get(scrub(party_type)):
- select_fields = "sum(debit_in_account_currency) as debit, sum(credit_in_account_currency) as credit"
+ # Invoices booked via Journal Entries
+ journal_entries = frappe.db.sql("""
+ select name, due_date, bill_no, bill_date
+ from `tabJournal Entry`
+ where posting_date <= %s
+ """, self.filters.report_date, as_dict=1)
+
+ for je in journal_entries:
+ if je.bill_no:
+ self.invoice_details.setdefault(je.name, je)
+
+ def set_party_details(self, row):
+ # customer / supplier name
+ party_details = self.get_party_details(row.party)
+ row.update(party_details)
+
+ if self.filters.get(scrub(self.filters.party_type)):
+ row.currency = row.account_currency
else:
- select_fields = "sum(debit) as debit, sum(credit) as credit"
+ row.currency = self.company_currency
- if date and not for_future:
- conditions += " and posting_date <= '%s'" % date
+ def allocate_outstanding_based_on_payment_terms(self, row):
+ self.get_payment_terms(row)
+ for term in row.payment_terms:
+ term.outstanding = term.invoiced
- if date and for_future:
- conditions += " and posting_date > '%s'" % date
+ # update "paid" and "oustanding" for this term
+ self.allocate_closing_to_term(row, term, 'paid')
+
+ # update "credit_note" and "oustanding" for this term
+ if term.outstanding:
+ self.allocate_closing_to_term(row, term, 'credit_note')
+
+ def get_payment_terms(self, row):
+ # build payment_terms for row
+ payment_terms_details = frappe.db.sql("""
+ select
+ si.name, si.party_account_currency, si.currency, si.conversion_rate,
+ ps.due_date, ps.payment_amount, ps.description
+ from `tab{0}` si, `tabPayment Schedule` ps
+ where
+ si.name = ps.parent and
+ si.name = %s
+ order by ps.due_date
+ """.format(row.voucher_type), row.voucher_no, as_dict = 1)
+
+
+ original_row = frappe._dict(row)
+ row.payment_terms = []
+
+ # If no or single payment terms, no need to split the row
+ if len(payment_terms_details) <= 1:
+ return
+
+ for d in payment_terms_details:
+ term = frappe._dict(original_row)
+ self.append_payment_term(row, d, term)
+
+ def append_payment_term(self, row, d, term):
+ if self.filters.get("customer") and d.currency == d.party_account_currency:
+ invoiced = d.payment_amount
+ else:
+ invoiced = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision)
+
+ row.payment_terms.append(term.update({
+ "due_date": d.due_date,
+ "invoiced": invoiced,
+ "invoice_grand_total": row.invoiced,
+ "payment_term": d.description,
+ "paid": 0.0,
+ "credit_note": 0.0,
+ "outstanding": 0.0
+ }))
+
+ def allocate_closing_to_term(self, row, term, key):
+ if row[key]:
+ if row[key] > term.outstanding:
+ term[key] = term.outstanding
+ row[key] -= term.outstanding
+ else:
+ term[key] = row[key]
+ row[key] = 0
+ term.outstanding -= term[key]
+
+ def allocate_extra_payments_or_credits(self, row):
+ # allocate extra payments / credits
+ additional_row = None
+ for key in ('paid', 'credit_note'):
+ if row[key] > 0:
+ if not additional_row:
+ additional_row = frappe._dict(row)
+ additional_row.invoiced = 0.0
+ additional_row[key] = row[key]
+
+ if additional_row:
+ additional_row.outstanding = additional_row.invoiced - additional_row.paid - additional_row.credit_note
+ self.append_row(additional_row)
+
+ def get_future_payments(self):
+ if self.filters.show_future_payments:
+ self.future_payments = frappe._dict()
+ future_payments = list(self.get_future_payments_from_payment_entry())
+ future_payments += list(self.get_future_payments_from_journal_entry())
+ if future_payments:
+ for d in future_payments:
+ if d.future_amount and d.invoice_no:
+ self.future_payments.setdefault((d.invoice_no, d.party), []).append(d)
+
+ def get_future_payments_from_payment_entry(self):
+ return frappe.db.sql("""
+ select
+ ref.reference_name as invoice_no,
+ payment_entry.party,
+ payment_entry.party_type,
+ payment_entry.posting_date as future_date,
+ ref.allocated_amount as future_amount,
+ payment_entry.reference_no as future_ref
+ from
+ `tabPayment Entry` as payment_entry inner join `tabPayment Entry Reference` as ref
+ on
+ (ref.parent = payment_entry.name)
+ where
+ payment_entry.docstatus = 1
+ and payment_entry.posting_date > %s
+ and payment_entry.party_type = %s
+ """, (self.filters.report_date, self.party_type), as_dict=1)
+
+ def get_future_payments_from_journal_entry(self):
+ if self.filters.get('party'):
+ amount_field = ("jea.debit_in_account_currency - jea.credit_in_account_currency"
+ if self.party_type == 'Supplier' else "jea.credit_in_account_currency - jea.debit_in_account_currency")
+ else:
+ amount_field = ("jea.debit - " if self.party_type == 'Supplier' else "jea.credit")
+
+ return frappe.db.sql("""
+ select
+ jea.reference_name as invoice_no,
+ jea.party,
+ jea.party_type,
+ je.posting_date as future_date,
+ sum({0}) as future_amount,
+ je.cheque_no as future_ref
+ from
+ `tabJournal Entry` as je inner join `tabJournal Entry Account` as jea
+ on
+ (jea.parent = je.name)
+ where
+ je.docstatus = 1
+ and je.posting_date > %s
+ and jea.party_type = %s
+ and jea.reference_name is not null and jea.reference_name != ''
+ group by je.name, jea.reference_name
+ having future_amount > 0
+ """.format(amount_field), (self.filters.report_date, self.party_type), as_dict=1)
+
+ def allocate_future_payments(self, row):
+ # future payments are captured in additional columns
+ # this method allocates pending future payments against a voucher to
+ # the current row (which could be generated from payment terms)
+ if not self.filters.show_future_payments:
+ return
+
+ row.remaining_balance = row.outstanding
+ row.future_amount = 0.0
+ for future in self.future_payments.get((row.voucher_no, row.party), []):
+ if row.remaining_balance > 0 and future.future_amount:
+ if future.future_amount > row.outstanding:
+ row.future_amount = row.outstanding
+ future.future_amount = future.future_amount - row.outstanding
+ row.remaining_balance = 0
+ else:
+ row.future_amount += future.future_amount
+ future.future_amount = 0
+ row.remaining_balance = row.outstanding - row.future_amount
+
+ row.setdefault('future_ref', []).append(cstr(future.future_ref) + '/' + cstr(future.future_date))
+
+ if row.future_ref:
+ row.future_ref = ', '.join(row.future_ref)
+
+ def set_ageing(self, row):
+ if self.filters.ageing_based_on == "Due Date":
+ entry_date = row.due_date
+ elif self.filters.ageing_based_on == "Supplier Invoice Date":
+ entry_date = row.bill_date
+ else:
+ entry_date = row.posting_date
+
+ self.get_ageing_data(entry_date, row)
+
+ # ageing buckets should not have amounts if due date is not reached
+ if getdate(entry_date) > getdate(self.filters.report_date):
+ row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0
+
+ def get_ageing_data(self, entry_date, row):
+ # [0-30, 30-60, 60-90, 90-120, 120-above]
+ row.range1 = row.range2 = row.range3 = row.range4 = range5 = 0.0
+
+ if not (self.age_as_on and entry_date):
+ return
+
+ row.age = (getdate(self.age_as_on) - getdate(entry_date)).days or 0
+ index = None
+ for i, days in enumerate([self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4]):
+ if row.age <= days:
+ index = i
+ break
+
+ if index is None: index = 4
+ row['range' + str(index+1)] = row.outstanding
+
+ def get_gl_entries(self):
+ # get all the GL entries filtered by the given filters
+
+ conditions, values = self.prepare_conditions()
+
+ if self.filters.get(scrub(self.party_type)):
+ select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
+ else:
+ select_fields = "debit, credit"
self.gl_entries = frappe.db.sql("""
select
@@ -477,91 +471,95 @@ class ReceivablePayableReport(object):
from
`tabGL Entry`
where
- docstatus < 2 and party_type=%s and (party is not null and party != '') {1}
- group by voucher_type, voucher_no, against_voucher_type, against_voucher, party
- order by posting_date, party"""
+ docstatus < 2
+ and party_type=%s
+ and (party is not null and party != '')
+ and posting_date <= %s
+ {1}
+ order by posting_date, party"""
.format(select_fields, conditions), values, as_dict=True)
- return self.gl_entries
-
- def prepare_conditions(self, party_type):
+ def prepare_conditions(self):
conditions = [""]
- values = [party_type]
+ values = [self.party_type, self.filters.report_date]
+ party_type_field = scrub(self.party_type)
- party_type_field = scrub(party_type)
+ self.add_common_filters(conditions, values, party_type_field)
+ if party_type_field=="customer":
+ self.add_customer_filters(conditions, values)
+
+ elif party_type_field=="supplier":
+ self.add_supplier_filters(conditions, values)
+
+ self.add_accounting_dimensions_filters()
+
+ return " and ".join(conditions), values
+
+ def add_common_filters(self, conditions, values, party_type_field):
if self.filters.company:
conditions.append("company=%s")
values.append(self.filters.company)
if self.filters.finance_book:
- conditions.append("ifnull(finance_book,'') in (%s, '')")
+ conditions.append("ifnull(finance_book, '') in (%s, '')")
values.append(self.filters.finance_book)
if self.filters.get(party_type_field):
conditions.append("party=%s")
values.append(self.filters.get(party_type_field))
- if party_type_field=="customer":
- account_type = "Receivable"
- if self.filters.get("customer_group"):
- lft, rgt = frappe.db.get_value("Customer Group",
- self.filters.get("customer_group"), ["lft", "rgt"])
+ # get GL with "receivable" or "payable" account_type
+ account_type = "Receivable" if self.party_type == "Customer" else "Payable"
+ accounts = [d.name for d in frappe.get_all("Account",
+ filters={"account_type": account_type, "company": self.filters.company})]
+ conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
+ values += accounts
- conditions.append("""party in (select name from tabCustomer
- where exists(select name from `tabCustomer Group` where lft >= {0} and rgt <= {1}
- and name=tabCustomer.customer_group))""".format(lft, rgt))
+ def add_customer_filters(self, conditions, values):
+ if self.filters.get("customer_group"):
+ conditions.append(self.get_hierarchical_filters('Customer Group', 'customer_group'))
- if self.filters.get("territory"):
- lft, rgt = frappe.db.get_value("Territory",
- self.filters.get("territory"), ["lft", "rgt"])
+ if self.filters.get("territory"):
+ conditions.append(self.get_hierarchical_filters('Territory', 'territory'))
- conditions.append("""party in (select name from tabCustomer
- where exists(select name from `tabTerritory` where lft >= {0} and rgt <= {1}
- and name=tabCustomer.territory))""".format(lft, rgt))
+ if self.filters.get("payment_terms_template"):
+ conditions.append("party in (select name from tabCustomer where payment_terms=%s)")
+ values.append(self.filters.get("payment_terms_template"))
- if self.filters.get("payment_terms_template"):
- conditions.append("party in (select name from tabCustomer where payment_terms=%s)")
- values.append(self.filters.get("payment_terms_template"))
+ if self.filters.get("sales_partner"):
+ conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)")
+ values.append(self.filters.get("sales_partner"))
- if self.filters.get("sales_partner"):
- conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)")
- values.append(self.filters.get("sales_partner"))
+ if self.filters.get("sales_person"):
+ lft, rgt = frappe.db.get_value("Sales Person",
+ self.filters.get("sales_person"), ["lft", "rgt"])
- if self.filters.get("sales_person"):
- lft, rgt = frappe.db.get_value("Sales Person",
- self.filters.get("sales_person"), ["lft", "rgt"])
+ conditions.append("""exists(select name from `tabSales Team` steam where
+ steam.sales_person in (select name from `tabSales Person` where lft >= {0} and rgt <= {1})
+ and ((steam.parent = voucher_no and steam.parenttype = voucher_type)
+ or (steam.parent = against_voucher and steam.parenttype = against_voucher_type)
+ or (steam.parent = party and steam.parenttype = 'Customer')))""".format(lft, rgt))
- conditions.append("""exists(select name from `tabSales Team` steam where
- steam.sales_person in (select name from `tabSales Person` where lft >= {0} and rgt <= {1})
- and ((steam.parent = voucher_no and steam.parenttype = voucher_type)
- or (steam.parent = against_voucher and steam.parenttype = against_voucher_type)
- or (steam.parent = party and steam.parenttype = 'Customer')))""".format(lft, rgt))
+ def add_supplier_filters(self, conditions, values):
+ if self.filters.get("supplier_group"):
+ conditions.append("""party in (select name from tabSupplier
+ where supplier_group=%s)""")
+ values.append(self.filters.get("supplier_group"))
- elif party_type_field=="supplier":
- account_type = "Payable"
- if self.filters.get("supplier_group"):
- conditions.append("""party in (select name from tabSupplier
- where supplier_group=%s)""")
- values.append(self.filters.get("supplier_group"))
+ if self.filters.get("payment_terms_template"):
+ conditions.append("party in (select name from tabSupplier where payment_terms=%s)")
+ values.append(self.filters.get("payment_terms_template"))
- if self.filters.get("payment_terms_template"):
- conditions.append("party in (select name from tabSupplier where payment_terms=%s)")
- values.append(self.filters.get("payment_terms_template"))
+ def get_hierarchical_filters(self, doctype, key):
+ lft, rgt = frappe.db.get_value(doctype, self.filters.get(key), ["lft", "rgt"])
- if self.filters.get("cost_center"):
- lft, rgt = frappe.get_cached_value("Cost Center",
- self.filters.get("cost_center"), ['lft', 'rgt'])
-
- conditions.append("""cost_center in (select name from `tabCost Center` where
- lft >= {0} and rgt <= {1})""".format(lft, rgt))
-
- if self.filters.company:
- accounts = [d.name for d in frappe.get_all("Account",
- filters={"account_type": account_type, "company": self.filters.company})]
- conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
- values += accounts
+ return """party in (select name from tabCustomer
+ where exists(select name from `tab{doctype}` where lft >= {lft} and rgt <= {rgt}
+ and name=tabCustomer.{key}))""".format(
+ doctype=doctype, lft=lft, rgt=rgt, key=key)
+ def add_accounting_dimensions_filters(self, conditions, values):
accounting_dimensions = get_accounting_dimensions()
if accounting_dimensions:
@@ -570,195 +568,133 @@ class ReceivablePayableReport(object):
conditions.append("{0} = %s".format(dimension))
values.append(self.filters.get(dimension))
- return " and ".join(conditions), values
+ def get_gle_balance(self, gle):
+ # get the balance of the GL (debit - credit) or reverse balance based on report type
+ return gle.get(self.dr_or_cr) - self.get_reverse_balance(gle)
- def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher):
- if not hasattr(self, "gl_entries_map"):
- self.gl_entries_map = {}
- for gle in self.get_gl_entries(party_type):
- if gle.against_voucher_type and gle.against_voucher:
- self.gl_entries_map.setdefault(gle.party, {})\
- .setdefault(gle.against_voucher_type, {})\
- .setdefault(gle.against_voucher, [])\
- .append(gle)
+ def get_reverse_balance(self, gle):
+ # get "credit" balance if report type is "debit" and vice versa
+ return gle.get('debit' if self.dr_or_cr=='credit' else 'credit')
- return self.gl_entries_map.get(party, {})\
- .get(against_voucher_type, {})\
- .get(against_voucher, [])
+ def is_invoice(self, gle):
+ if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'):
+ return True
- def get_payment_term_detail(self, voucher_nos):
- payment_term_map = frappe._dict()
- payment_terms_details = frappe.db.sql(""" select si.name,
- party_account_currency, currency, si.conversion_rate,
- ps.due_date, ps.payment_amount, ps.description
- from `tabSales Invoice` si, `tabPayment Schedule` ps
- where si.name = ps.parent and
- si.docstatus = 1 and si.company = %s and
- si.name in (%s) order by ps.due_date
- """ % (frappe.db.escape(self.filters.company), ','.join(['%s'] *len(voucher_nos))),
- (tuple(voucher_nos)), as_dict = 1)
-
- for d in payment_terms_details:
- if self.filters.get("customer") and d.currency == d.party_account_currency:
- payment_term_amount = d.payment_amount
+ def get_party_details(self, party):
+ if not party in self.party_details:
+ if self.party_type == 'Customer':
+ self.party_details[party] = frappe.db.get_value('Customer', party, ['customer_name',
+ 'territory', 'customer_group', 'customer_primary_contact'], as_dict=True)
else:
- payment_term_amount = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision)
+ self.party_details[party] = frappe.db.get_value('Supplier', party, ['supplier_name',
+ 'supplier_group'], as_dict=True)
- payment_term_map.setdefault(d.name, []).append(frappe._dict({
- "due_date": d.due_date,
- "payment_term_amount": payment_term_amount,
- "description": d.description
- }))
- return payment_term_map
+ return self.party_details[party]
- def get_chart_data(self, columns, data):
- ageing_columns = columns[self.ageing_col_idx_start : self.ageing_col_idx_start+5]
+ def get_columns(self):
+ self.columns = []
+ self.add_column('Posting Date', fieldtype='Date')
+ self.add_column(label=_(self.party_type), fieldname='party',
+ fieldtype='Link', options=self.party_type, width=180)
+
+ if self.party_naming_by == "Naming Series":
+ self.add_column(_('{0} Name').format(self.party_type),
+ fieldname = scrub(self.party_type) + '_name', fieldtype='Data')
+
+ if self.party_type == 'Customer':
+ self.add_column(_("Customer Contact"), fieldname='customer_primary_contact',
+ fieldtype='Link', options='Contact')
+
+ self.add_column(label=_('Voucher Type'), fieldname='voucher_type', fieldtype='Data')
+ self.add_column(label=_('Voucher No'), fieldname='voucher_no', fieldtype='Dynamic Link',
+ options='voucher_type', width=180)
+ self.add_column(label='Due Date', fieldtype='Date')
+
+ if self.party_type == "Supplier":
+ self.add_column(label=_('Bill No'), fieldname='bill_no', fieldtype='Data')
+ self.add_column(label=_('Bill Date'), fieldname='bill_date', fieldtype='Date')
+
+ if self.filters.based_on_payment_terms:
+ self.add_column(label=_('Payment Term'), fieldname='payment_term', fieldtype='Data')
+ self.add_column(label=_('Invoice Grand Total'), fieldname='invoice_grand_total')
+
+ self.add_column(_('Invoiced Amount'), fieldname='invoiced')
+ self.add_column(_('Paid Amount'), fieldname='paid')
+ if self.party_type == "Customer":
+ self.add_column(_('Credit Note'), fieldname='credit_note')
+ else:
+ # note: fieldname is still `credit_note`
+ self.add_column(_('Debit Note'), fieldname='credit_note')
+ self.add_column(_('Outstanding Amount'), fieldname='outstanding')
+
+ self.setup_ageing_columns()
+
+ self.add_column(label=_('Currency'), fieldname='currency', fieldtype='Link', options='Currency', width=80)
+
+ if self.filters.show_future_payments:
+ self.add_column(label=_('Future Payment Ref'), fieldname='future_ref', fieldtype='Data')
+ self.add_column(label=_('Future Payment Amount'), fieldname='future_amount')
+ self.add_column(label=_('Remaining Balance'), fieldname='remaining_balance')
+
+ if self.filters.party_type == 'Customer':
+ self.add_column(label=_('Customer LPO'), fieldname='po_no', fieldtype='Data')
+
+ # comma separated list of linked delivery notes
+ if self.filters.show_delivery_notes:
+ self.add_column(label=_('Delivery Notes'), fieldname='delivery_notes', fieldtype='Data')
+ self.add_column(label=_('Territory'), fieldname='territory', fieldtype='Link',
+ options='Territory')
+ self.add_column(label=_('Customer Group'), fieldname='customer_group', fieldtype='Link',
+ options='Customer Group')
+ if self.filters.show_sales_person:
+ self.add_column(label=_('Sales Person'), fieldname='sales_person', fieldtype='Data')
+
+ if self.filters.party_type == "Supplier":
+ self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link',
+ options='Supplier Group')
+
+ self.add_column(label=_('Remarks'), fieldname='remarks', fieldtype='Text', width=200)
+
+ def add_column(self, label, fieldname=None, fieldtype='Currency', options=None, width=120):
+ if not fieldname: fieldname = scrub(label)
+ if fieldtype=='Currency': options='currency'
+ if fieldtype=='Date': width = 90
+
+ self.columns.append(dict(
+ label=label,
+ fieldname=fieldname,
+ fieldtype=fieldtype,
+ options=options,
+ width=width
+ ))
+
+ def setup_ageing_columns(self):
+ # for charts
+ self.ageing_column_labels = []
+ self.add_column(label=_('Age (Days)'), fieldname='age', fieldtype='Int', width=80)
+
+ for i, label in enumerate(["0-{range1}".format(range1=self.filters["range1"]),
+ "{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
+ "{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
+ "{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
+ "{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]):
+ self.add_column(label=label, fieldname='range' + str(i+1))
+ self.ageing_column_labels.append(label)
+
+ def get_chart_data(self):
rows = []
- for d in data:
- values = d[self.ageing_col_idx_start : self.ageing_col_idx_start+5]
- precision = cint(frappe.db.get_default("float_precision")) or 2
- formatted_values = [frappe.utils.rounded(val, precision) for val in values]
+ for row in self.data:
rows.append(
{
- 'values': formatted_values
+ 'values': [row.range1, row.range2, row.range3, row.range4, row.range5]
}
)
- return {
+ self.chart = {
"data": {
- 'labels': [d.get("label") for d in ageing_columns],
+ 'labels': self.ageing_column_labels,
'datasets': rows
},
"type": 'percentage'
- }
-
-def execute(filters=None):
- args = {
- "party_type": "Customer",
- "naming_by": ["Selling Settings", "cust_master_name"],
- }
- return ReceivablePayableReport(filters).run(args)
-
-def get_ageing_data(first_range, second_range, third_range,
- fourth_range, age_as_on, entry_date, outstanding_amount):
- # [0-30, 30-60, 60-90, 90-120, 120-above]
- outstanding_range = [0.0, 0.0, 0.0, 0.0, 0.0]
-
- if not (age_as_on and entry_date):
- return [0] + outstanding_range
-
- age = (getdate(age_as_on) - getdate(entry_date)).days or 0
- index = None
- for i, days in enumerate([first_range, second_range, third_range, fourth_range]):
- if age <= days:
- index = i
- break
-
- if index is None: index = 4
- outstanding_range[index] = outstanding_amount
-
- return [age] + outstanding_range
-
-def get_pdc_details(party_type, report_date):
- pdc_details = frappe._dict()
- pdc_via_pe = frappe.db.sql("""
- select
- pref.reference_name as invoice_no, pent.party, pent.party_type,
- pent.posting_date as pdc_date, ifnull(pref.allocated_amount,0) as pdc_amount,
- pent.reference_no as pdc_ref
- from
- `tabPayment Entry` as pent inner join `tabPayment Entry Reference` as pref
- on
- (pref.parent = pent.name)
- where
- pent.docstatus < 2 and pent.posting_date > %s
- and pent.party_type = %s
- """, (report_date, party_type), as_dict=1)
-
- for pdc in pdc_via_pe:
- pdc_details.setdefault((pdc.invoice_no, pdc.party), []).append(pdc)
-
- if scrub(party_type):
- amount_field = ("jea.debit_in_account_currency"
- if party_type == 'Supplier' else "jea.credit_in_account_currency")
- else:
- amount_field = "jea.debit + jea.credit"
-
- pdc_via_je = frappe.db.sql("""
- select
- jea.reference_name as invoice_no, jea.party, jea.party_type,
- je.posting_date as pdc_date, ifnull({0},0) as pdc_amount,
- je.cheque_no as pdc_ref
- from
- `tabJournal Entry` as je inner join `tabJournal Entry Account` as jea
- on
- (jea.parent = je.name)
- where
- je.docstatus < 2 and je.posting_date > %s
- and jea.party_type = %s
- """.format(amount_field), (report_date, party_type), as_dict=1)
-
- for pdc in pdc_via_je:
- pdc_details.setdefault((pdc.invoice_no, pdc.party), []).append(pdc)
-
- return pdc_details
-
-def get_dn_details(party_type, voucher_nos):
- dn_details = frappe._dict()
-
- if party_type == "Customer":
- for si in frappe.db.sql("""
- select
- parent, GROUP_CONCAT(delivery_note SEPARATOR ', ') as dn
- from
- `tabSales Invoice Item`
- where
- docstatus=1 and delivery_note is not null and delivery_note != ''
- and parent in (%s) group by parent
- """ %(','.join(['%s'] * len(voucher_nos))), tuple(voucher_nos) , as_dict=1):
- dn_details.setdefault(si.parent, si.dn)
-
- for si in frappe.db.sql("""
- select
- against_sales_invoice as parent, GROUP_CONCAT(parent SEPARATOR ', ') as dn
- from
- `tabDelivery Note Item`
- where
- docstatus=1 and against_sales_invoice is not null and against_sales_invoice != ''
- and against_sales_invoice in (%s)
- group by against_sales_invoice
- """ %(','.join(['%s'] * len(voucher_nos))), tuple(voucher_nos) , as_dict=1):
- if si.parent in dn_details:
- dn_details[si.parent] += ', %s' %(si.dn)
- else:
- dn_details.setdefault(si.parent, si.dn)
-
- return dn_details
-
-def get_voucher_details(party_type, voucher_nos, dn_details):
- voucher_details = frappe._dict()
-
- if party_type == "Customer":
- for si in frappe.db.sql("""
- select inv.name, inv.due_date, inv.po_no, GROUP_CONCAT(steam.sales_person SEPARATOR ', ') as sales_person
- from `tabSales Invoice` inv
- left join `tabSales Team` steam on steam.parent = inv.name and steam.parenttype = 'Sales Invoice'
- where inv.docstatus=1 and inv.name in (%s)
- group by inv.name
- """ %(','.join(['%s'] *len(voucher_nos))), (tuple(voucher_nos)), as_dict=1):
- si['delivery_note'] = dn_details.get(si.name)
- voucher_details.setdefault(si.name, si)
-
- if party_type == "Supplier":
- for pi in frappe.db.sql("""select name, due_date, bill_no, bill_date
- from `tabPurchase Invoice` where docstatus = 1 and name in (%s)
- """ %(','.join(['%s'] *len(voucher_nos))), (tuple(voucher_nos)), as_dict=1):
- voucher_details.setdefault(pi.name, pi)
-
- for pi in frappe.db.sql("""select name, due_date, bill_no, bill_date from
- `tabJournal Entry` where docstatus = 1 and bill_no is not NULL and name in (%s)
- """ %(','.join(['%s'] *len(voucher_nos))), (tuple(voucher_nos)), as_dict=1):
- voucher_details.setdefault(pi.name, pi)
-
- return voucher_details
+ }
\ No newline at end of file
diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
index 43786a4446..f0274b4472 100644
--- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
@@ -14,33 +14,44 @@ class TestAccountsReceivable(unittest.TestCase):
filters = {
'company': '_Test Company 2',
- 'based_on_payment_terms': 1
+ 'based_on_payment_terms': 1,
+ 'report_date': today(),
+ 'range1': 30,
+ 'range2': 60,
+ 'range3': 90,
+ 'range4': 120
}
+ # check invoice grand total and invoiced column's value for 3 payment terms
name = make_sales_invoice()
report = execute(filters)
- expected_data = [[100,30], [100,50], [100,20]]
+ expected_data = [[100, 30], [100, 50], [100, 20]]
- self.assertEqual(expected_data[0], report[1][0][7:9])
- self.assertEqual(expected_data[1], report[1][1][7:9])
- self.assertEqual(expected_data[2], report[1][2][7:9])
+ for i in range(3):
+ row = report[1][i-1]
+ self.assertEqual(expected_data[i-1], [row.invoice_grand_total, row.invoiced])
+ # check invoice grand total, invoiced, paid and outstanding column's value after payment
make_payment(name)
report = execute(filters)
- expected_data_after_payment = [[100,50], [100,20]]
+ expected_data_after_payment = [[100, 50, 10, 40], [100, 20, 0, 20]]
- self.assertEqual(expected_data_after_payment[0], report[1][0][7:9])
- self.assertEqual(expected_data_after_payment[1], report[1][1][7:9])
+ for i in range(2):
+ row = report[1][i-1]
+ self.assertEqual(expected_data_after_payment[i-1],
+ [row.invoice_grand_total, row.invoiced, row.paid, row.outstanding])
+ # check invoice grand total, invoiced, paid and outstanding column's value after credit note
make_credit_note(name)
report = execute(filters)
- expected_data_after_credit_note = [[100,100,30,100,-30]]
-
- self.assertEqual(expected_data_after_credit_note[0], report[1][0][7:12])
+ expected_data_after_credit_note = [100, 0, 0, 40, -40]
+ row = report[1][0]
+ self.assertEqual(expected_data_after_credit_note,
+ [row.invoice_grand_total, row.invoiced, row.paid, row.credit_note, row.outstanding])
def make_sales_invoice():
frappe.set_user("Administrator")
@@ -64,7 +75,7 @@ def make_sales_invoice():
return si.name
def make_payment(docname):
- pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=30)
+ pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=40)
pe.paid_from = "Debtors - _TC2"
pe.insert()
pe.submit()
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
index ec24aece5f..350e081957 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
@@ -3,236 +3,11 @@
from __future__ import unicode_literals
import frappe
-from frappe import _, scrub
-from frappe.utils import flt
+from frappe import _
+from frappe.utils import flt, cint
from erpnext.accounts.party import get_partywise_advanced_payment_amount
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
-
from six import iteritems
-from six.moves import zip
-
-class AccountsReceivableSummary(ReceivablePayableReport):
- def run(self, args):
- party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
- return self.get_columns(party_naming_by, args), self.get_data(party_naming_by, args)
-
- def get_columns(self, party_naming_by, args):
- columns = [_(args.get("party_type")) + ":Link/" + args.get("party_type") + ":200"]
-
- if party_naming_by == "Naming Series":
- columns += [ args.get("party_type") + " Name::140"]
-
- credit_debit_label = "Credit Note Amt" if args.get('party_type') == 'Customer' else "Debit Note Amt"
-
- columns += [{
- "label": _("Advance Amount"),
- "fieldname": "advance_amount",
- "fieldtype": "Currency",
- "options": "currency",
- "width": 100
- },{
- "label": _("Total Invoiced Amt"),
- "fieldname": "total_invoiced_amt",
- "fieldtype": "Currency",
- "options": "currency",
- "width": 100
- },
- {
- "label": _("Total Paid Amt"),
- "fieldname": "total_paid_amt",
- "fieldtype": "Currency",
- "options": "currency",
- "width": 100
- }]
-
- columns += [
- {
- "label": _(credit_debit_label),
- "fieldname": scrub(credit_debit_label),
- "fieldtype": "Currency",
- "options": "currency",
- "width": 140
- },
- {
- "label": _("Total Outstanding Amt"),
- "fieldname": "total_outstanding_amt",
- "fieldtype": "Currency",
- "options": "currency",
- "width": 160
- },
- {
- "label": _("0-" + str(self.filters.range1)),
- "fieldname": scrub("0-" + str(self.filters.range1)),
- "fieldtype": "Currency",
- "options": "currency",
- "width": 160
- },
- {
- "label": _(str(self.filters.range1) + "-" + str(self.filters.range2)),
- "fieldname": scrub(str(self.filters.range1) + "-" + str(self.filters.range2)),
- "fieldtype": "Currency",
- "options": "currency",
- "width": 160
- },
- {
- "label": _(str(self.filters.range2) + "-" + str(self.filters.range3)),
- "fieldname": scrub(str(self.filters.range2) + "-" + str(self.filters.range3)),
- "fieldtype": "Currency",
- "options": "currency",
- "width": 160
- },
- {
- "label": _(str(self.filters.range3) + "-" + str(self.filters.range4)),
- "fieldname": scrub(str(self.filters.range3) + "-" + str(self.filters.range4)),
- "fieldtype": "Currency",
- "options": "currency",
- "width": 160
- },
- {
- "label": _(str(self.filters.range4) + _("-Above")),
- "fieldname": scrub(str(self.filters.range4) + _("-Above")),
- "fieldtype": "Currency",
- "options": "currency",
- "width": 160
- }
- ]
-
- if args.get("party_type") == "Customer":
- columns += [{
- "label": _("Territory"),
- "fieldname": "territory",
- "fieldtype": "Link",
- "options": "Territory",
- "width": 80
- },
- {
- "label": _("Customer Group"),
- "fieldname": "customer_group",
- "fieldtype": "Link",
- "options": "Customer Group",
- "width": 80
- },
- {
- "label": _("Sales Person"),
- "fieldtype": "Data",
- "fieldname": "sales_person",
- "width": 120,
- }]
-
- if args.get("party_type") == "Supplier":
- columns += [{
- "label": _("Supplier Group"),
- "fieldname": "supplier_group",
- "fieldtype": "Link",
- "options": "Supplier Group",
- "width": 80
- }]
-
- columns.append({
- "fieldname": "currency",
- "label": _("Currency"),
- "fieldtype": "Link",
- "options": "Currency",
- "width": 80
- })
-
- return columns
-
- def get_data(self, party_naming_by, args):
- data = []
-
- partywise_total = self.get_partywise_total(party_naming_by, args)
-
- partywise_advance_amount = get_partywise_advanced_payment_amount(args.get("party_type"),
- self.filters.get("report_date")) or {}
- for party, party_dict in iteritems(partywise_total):
- row = [party]
-
- if party_naming_by == "Naming Series":
- row += [self.get_party_name(args.get("party_type"), party)]
-
- row += [partywise_advance_amount.get(party, 0)]
-
- paid_amt = 0
- if party_dict.paid_amt > 0:
- paid_amt = flt(party_dict.paid_amt - partywise_advance_amount.get(party, 0))
-
- row += [
- party_dict.invoiced_amt, paid_amt, party_dict.credit_amt, party_dict.outstanding_amt,
- party_dict.range1, party_dict.range2, party_dict.range3, party_dict.range4, party_dict.range5
- ]
-
- if args.get("party_type") == "Customer":
- row += [self.get_territory(party), self.get_customer_group(party), ", ".join(set(party_dict.sales_person))]
- if args.get("party_type") == "Supplier":
- row += [self.get_supplier_group(party)]
-
- row.append(party_dict.currency)
- data.append(row)
-
- return data
-
- def get_partywise_total(self, party_naming_by, args):
- party_total = frappe._dict()
- for d in self.get_voucherwise_data(party_naming_by, args):
- party_total.setdefault(d.party,
- frappe._dict({
- "invoiced_amt": 0,
- "paid_amt": 0,
- "credit_amt": 0,
- "outstanding_amt": 0,
- "range1": 0,
- "range2": 0,
- "range3": 0,
- "range4": 0,
- "range5": 0,
- "sales_person": []
- })
- )
- for k in list(party_total[d.party]):
- if k not in ["currency", "sales_person"]:
- party_total[d.party][k] += flt(d.get(k, 0))
-
- party_total[d.party].currency = d.currency
-
- if d.sales_person:
- party_total[d.party].sales_person.append(d.sales_person)
-
- return party_total
-
- def get_voucherwise_data(self, party_naming_by, args):
- voucherwise_data = ReceivablePayableReport(self.filters).run(args)[1]
-
- cols = ["posting_date", "party"]
-
- if party_naming_by == "Naming Series":
- cols += ["party_name"]
-
- if args.get("party_type") == 'Customer':
- cols += ["contact"]
-
- cols += ["voucher_type", "voucher_no", "due_date"]
-
- if args.get("party_type") == "Supplier":
- cols += ["bill_no", "bill_date"]
-
- cols += ["invoiced_amt", "paid_amt", "credit_amt",
- "outstanding_amt", "age", "range1", "range2", "range3", "range4", "range5", "currency", "pdc/lc_date", "pdc/lc_ref",
- "pdc/lc_amount"]
-
- if args.get("party_type") == "Supplier":
- cols += ["supplier_group", "remarks"]
- if args.get("party_type") == "Customer":
- cols += ["po_no", "do_no", "territory", "customer_group", "sales_person", "remarks"]
-
- return self.make_data_dict(cols, voucherwise_data)
-
- def make_data_dict(self, cols, data):
- data_dict = []
- for d in data:
- data_dict.append(frappe._dict(zip(cols, d)))
-
- return data_dict
def execute(filters=None):
args = {
@@ -241,3 +16,119 @@ def execute(filters=None):
}
return AccountsReceivableSummary(filters).run(args)
+
+class AccountsReceivableSummary(ReceivablePayableReport):
+ def run(self, args):
+ self.party_type = args.get('party_type')
+ self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
+ self.get_columns()
+ self.get_data(args)
+ return self.columns, self.data
+
+ def get_data(self, args):
+ self.data = []
+
+ self.receivables = ReceivablePayableReport(self.filters).run(args)[1]
+
+ self.get_party_total(args)
+
+ party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
+ self.filters.report_date) or {}
+
+ for party, party_dict in iteritems(self.party_total):
+ row = frappe._dict()
+
+ row.party = party
+ if self.party_naming_by == "Naming Series":
+ row.party_name = frappe.get_cached_value(self.party_type, party, [self.party_type + "_name"])
+
+ row.update(party_dict)
+
+ # Advance against party
+ row.advance = party_advance_amount.get(party, 0)
+
+ # In AR/AP, advance shown in paid columns,
+ # but in summary report advance shown in separate column
+ row.paid -= row.advance
+
+ self.data.append(row)
+
+ def get_party_total(self, args):
+ self.party_total = frappe._dict()
+
+ for d in self.receivables:
+ self.init_party_total(d)
+
+ # Add all amount columns
+ for k in list(self.party_total[d.party]):
+ if k not in ["currency", "sales_person"]:
+
+ self.party_total[d.party][k] += d.get(k, 0.0)
+
+ # set territory, customer_group, sales person etc
+ self.set_party_details(d)
+
+ def init_party_total(self, row):
+ self.party_total.setdefault(row.party, frappe._dict({
+ "invoiced": 0.0,
+ "paid": 0.0,
+ "credit_note": 0.0,
+ "outstanding": 0.0,
+ "range1": 0.0,
+ "range2": 0.0,
+ "range3": 0.0,
+ "range4": 0.0,
+ "range5": 0.0,
+ "sales_person": []
+ }))
+
+ def set_party_details(self, row):
+ self.party_total[row.party].currency = row.currency
+
+ for key in ('territory', 'customer_group', 'supplier_group'):
+ if row.get(key):
+ self.party_total[row.party][key] = row.get(key)
+
+ if row.sales_person:
+ self.party_total[row.party].sales_person.append(row.sales_person)
+
+ def get_columns(self):
+ self.columns = []
+ self.add_column(label=_(self.party_type), fieldname='party',
+ fieldtype='Link', options=self.party_type, width=180)
+
+ if self.party_naming_by == "Naming Series":
+ self.add_column(_('{0} Name').format(self.party_type),
+ fieldname = 'party_name', fieldtype='Data')
+
+ credit_debit_label = "Credit Note" if self.party_type == 'Customer' else "Debit Note"
+
+ self.add_column(_('Advance Amount'), fieldname='advance')
+ self.add_column(_('Invoiced Amount'), fieldname='invoiced')
+ self.add_column(_('Paid Amount'), fieldname='paid')
+ self.add_column(_(credit_debit_label), fieldname='credit_note')
+ self.add_column(_('Outstanding Amount'), fieldname='outstanding')
+
+ self.setup_ageing_columns()
+
+ if self.party_type == "Customer":
+ self.add_column(label=_('Territory'), fieldname='territory', fieldtype='Link',
+ options='Territory')
+ self.add_column(label=_('Customer Group'), fieldname='customer_group', fieldtype='Link',
+ options='Customer Group')
+ if self.filters.show_sales_person:
+ self.add_column(label=_('Sales Person'), fieldname='sales_person', fieldtype='Data')
+ else:
+ self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link',
+ options='Supplier Group')
+
+ self.add_column(label=_('Currency'), fieldname='currency', fieldtype='Link',
+ options='Currency', width=80)
+
+ def setup_ageing_columns(self):
+ for i, label in enumerate(["0-{range1}".format(range1=self.filters["range1"]),
+ "{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
+ "{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
+ "{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
+ "{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]):
+ self.add_column(label=label, fieldname='range' + str(i+1))
\ No newline at end of file
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index d0b4ba0e65..3c9c3cca03 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -372,7 +372,7 @@ def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, fil
return frappe.db.sql("""
select `tabContact`.name from `tabContact`, `tabDynamic Link`
where `tabContact`.name = `tabDynamic Link`.parent and `tabDynamic Link`.link_name = %(customer)s
- and `tabDynamic Link`.link_doctype = 'Customer' and `tabContact`.is_primary_contact = 1
+ and `tabDynamic Link`.link_doctype = 'Customer'
and `tabContact`.name like %(txt)s
""", {
'customer': customer,
@@ -384,7 +384,7 @@ def get_customer_primary_address(doctype, txt, searchfield, start, page_len, fil
return frappe.db.sql("""
select `tabAddress`.name from `tabAddress`, `tabDynamic Link`
where `tabAddress`.name = `tabDynamic Link`.parent and `tabDynamic Link`.link_name = %(customer)s
- and `tabDynamic Link`.link_doctype = 'Customer' and `tabAddress`.is_primary_address = 1
+ and `tabDynamic Link`.link_doctype = 'Customer'
and `tabAddress`.name like %(txt)s
""", {
'customer': customer,
From a70e2bdbc9fa803f422f2ceed1ea9a86c9a5867a Mon Sep 17 00:00:00 2001
From: Sahil Khan
Date: Tue, 3 Sep 2019 16:20:01 +0530
Subject: [PATCH 121/122] fix(change_log): add v12_1_0 changelog
---
erpnext/change_log/v12/v12_1_0.md | 6 ++++++
1 file changed, 6 insertions(+)
create mode 100644 erpnext/change_log/v12/v12_1_0.md
diff --git a/erpnext/change_log/v12/v12_1_0.md b/erpnext/change_log/v12/v12_1_0.md
new file mode 100644
index 0000000000..aed09c7e39
--- /dev/null
+++ b/erpnext/change_log/v12/v12_1_0.md
@@ -0,0 +1,6 @@
+# Version 12.1.0 Release Notes
+
+### Stock
+
+1. [Pick List](https://erpnext.com/docs/user/manual/en/stock/pick-list)
+2. [Refactored Accounts Receivable Reports](https://erpnext.com/docs/user/manual/en/accounts/accounting-reports#2-accounting-statements)
From e8b1c8253139958a6d15b1beb5604ce47b0143b4 Mon Sep 17 00:00:00 2001
From: Sahil Khan
Date: Tue, 3 Sep 2019 16:53:06 +0550
Subject: [PATCH 122/122] bumped to version 12.1.0
---
erpnext/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 2490f3b42e..ee37c8d780 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
-__version__ = '12.0.8'
+__version__ = '12.1.0'
def get_default_company(user=None):
'''Get default company for user'''