Merge branch 'staging-fixes' into staging
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index bb94383..d50b5bf 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
-__version__ = '10.1.70'
+__version__ = '10.1.71'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 3da54f0..263b5bb 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -514,25 +514,9 @@
me.frm.set_df_property("apply_tds", "read_only", 1);
}
- $.each(["warehouse", "rejected_warehouse"], function(i, field) {
- frm.set_query(field, "items", function() {
- return {
- filters: [
- ["Warehouse", "company", "in", ["", cstr(frm.doc.company)]],
- ["Warehouse", "is_group", "=", 0]
- ]
- }
- })
- })
-
- frm.set_query("supplier_warehouse", function() {
- return {
- filters: [
- ["Warehouse", "company", "in", ["", cstr(frm.doc.company)]],
- ["Warehouse", "is_group", "=", 0]
- ]
- }
- })
+ erpnext.queries.setup_queries(frm, "Warehouse", function() {
+ return erpnext.queries.warehouse(frm.doc);
+ });
},
is_subcontracted: function(frm) {
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 504d45f..bd55c28 100755
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
@@ -1492,6 +1493,207 @@
"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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "update_stock",
+ "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 Accepted 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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "update_stock",
+ "description": "Warehouse where you are maintaining stock of rejected items",
+ "fieldname": "rejected_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": "Rejected Warehouse",
+ "length": 0,
+ "no_copy": 1,
+ "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
+ },
+ {
+ "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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "No",
+ "fieldname": "is_subcontracted",
+ "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": "Raw Materials Supplied",
+ "length": 0,
+ "no_copy": 0,
+ "options": "No\nYes",
+ "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_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.is_subcontracted==\"Yes\"",
+ "fieldname": "supplier_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": "Supplier Warehouse",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Warehouse",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "print_width": "50px",
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": "50px"
+ },
+ {
+ "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,
@@ -1552,6 +1754,38 @@
"unique": 0
},
{
+ "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
+ },
+ {
"allow_bulk_edit": 1,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
@@ -1591,6 +1825,73 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "collapsible_depends_on": "supplied_items",
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "raw_materials_supplied",
+ "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": "Raw Materials Supplied",
+ "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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "supplied_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": "Supplied Items",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Purchase Receipt Item Supplied",
+ "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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"columns": 0,
"fieldname": "section_break_26",
"fieldtype": "Section Break",
@@ -3728,140 +4029,6 @@
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "raw_materials_supplied",
- "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": "Raw Materials Supplied",
- "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
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "No",
- "fieldname": "is_subcontracted",
- "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": "Raw Materials Supplied",
- "length": 0,
- "no_copy": 0,
- "options": "No\nYes",
- "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_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "supplier_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": "Supplier Warehouse",
- "length": 0,
- "no_copy": 1,
- "options": "Warehouse",
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "print_width": "50px",
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0,
- "width": "50px"
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "supplied_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": "Supplied Items",
- "length": 0,
- "no_copy": 0,
- "options": "Purchase Receipt Item Supplied",
- "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
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "printing_settings",
@@ -4358,40 +4525,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "description": "Warehouse where you are maintaining stock of rejected items",
- "fieldname": "rejected_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": "Rejected Warehouse",
- "length": 0,
- "no_copy": 1,
- "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
- },
- {
- "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,
@@ -4593,7 +4726,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2018-09-11 14:44:31.220376",
+ "modified": "2018-11-13 19:55:58.018816",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index f2a5c16..7348e1f 100755
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -165,9 +165,12 @@
select
i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no,
i.has_serial_no, i.is_stock_item, i.brand, i.stock_uom, i.image,
- id.expense_account, id.selling_cost_center, id.default_warehouse
+ id.expense_account, id.selling_cost_center, id.default_warehouse,
+ i.sales_uom, c.conversion_factor
from
- `tabItem` i LEFT JOIN `tabItem Default` id ON id.parent = i.name and id.company = %s
+ `tabItem` i
+ left join `tabItem Default` id on id.parent = i.name and id.company = %s
+ left join `tabUOM Conversion Detail` c on i.name = c.parent and i.sales_uom = c.uom
where
i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1
{cond}
@@ -534,6 +537,7 @@
item_doc.item_name = item.get('item_name')
item_doc.description = item.get('description')
item_doc.stock_uom = item.get('stock_uom')
+ item_doc.uom = item.get('uom')
item_doc.item_group = item.get('item_group')
item_doc.append('item_defaults', {
"company": doc.get("company"),
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index b8ea205..a4588b3 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -939,7 +939,9 @@
dialog.set_primary_action(__('Add'), function() {
let checked_values = get_checked_values($results);
if(checked_values.length > 0){
- frm.set_value("patient", dialog.fields_dict.patient.input.value);
+ if(invoice_healthcare_services) {
+ frm.set_value("patient", dialog.fields_dict.patient.input.value);
+ }
frm.set_value("items", []);
add_to_item_line(frm, checked_values, invoice_healthcare_services);
dialog.hide();
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 4154d2e..09e952b 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
@@ -1592,6 +1593,71 @@
"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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "update_stock",
+ "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
+ },
+ {
+ "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,
@@ -1652,6 +1718,38 @@
"unique": 0
},
{
+ "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
+ },
+ {
"allow_bulk_edit": 1,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
@@ -5546,7 +5644,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2018-09-07 14:24:58.854289",
+ "modified": "2018-11-12 20:01:21.289303",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js
index 314b91b..91f3711 100755
--- a/erpnext/accounts/page/pos/pos.js
+++ b/erpnext/accounts/page/pos/pos.js
@@ -1407,6 +1407,8 @@
this.child.item_code = this.items[0].item_code;
this.child.item_name = this.items[0].item_name;
this.child.stock_uom = this.items[0].stock_uom;
+ this.child.uom = this.items[0].sales_uom || this.items[0].stock_uom;
+ this.child.conversion_factor = this.items[0].conversion_factor || 1;
this.child.brand = this.items[0].brand;
this.child.description = this.items[0].description || this.items[0].item_name;
this.child.discount_percentage = 0.0;
@@ -1416,8 +1418,8 @@
this.child.income_account = this.pos_profile_data['income_account'] || this.items[0].income_account;
this.child.warehouse = (this.item_serial_no[this.child.item_code]
? this.item_serial_no[this.child.item_code][1] : (this.pos_profile_data['warehouse'] || this.items[0].default_warehouse));
- this.child.price_list_rate = flt(this.price_list_data[this.child.item_code], 9) / flt(this.frm.doc.conversion_rate, 9);
- this.child.rate = flt(this.price_list_data[this.child.item_code], 9) / flt(this.frm.doc.conversion_rate, 9);
+ this.child.price_list_rate = flt(this.price_list_data[this.child.item_code] * this.child.conversion_factor, 9) / flt(this.frm.doc.conversion_rate, 9);
+ this.child.rate = flt(this.price_list_data[this.child.item_code] * this.child.conversion_factor, 9) / flt(this.frm.doc.conversion_rate, 9);
this.child.actual_qty = me.get_actual_qty(this.items[0]);
this.child.amount = flt(this.child.qty) * flt(this.child.rate);
this.child.batch_no = this.item_batch_no[this.child.item_code];
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 602e671..2826760 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -196,8 +196,9 @@
"fieldname":"group_by",
"label": __("Group by"),
"fieldtype": "Select",
- "options": ["", "Group by Voucher", "Group by Account", "Group by Party"],
- "default": "Group by Voucher"
+ "options": ["", __("Group by Voucher"), __("Group by Voucher (Consolidated)"),
+ __("Group by Account"), __("Group by Party")],
+ "default": __("Group by Voucher (Consolidated)")
},
{
"fieldname":"tax_id",
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 56663d3..524f5f7 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -16,6 +16,8 @@
return [], []
account_details = {}
+ if not filters.get("group_by"):
+ filters['group_by'] = _('Group by Voucher (Consolidated)')
if filters and filters.get('print_in_account_currency') and \
not filters.get('account'):
@@ -48,11 +50,12 @@
if filters.get("account") and not account_details.get(filters.account):
frappe.throw(_("Account {0} does not exists").format(filters.account))
- if (filters.get("account") and filters.get("group_by") == 'Group by Account'
+ if (filters.get("account") and filters.get("group_by") == _('Group by Account')
and account_details[filters.account].is_group == 0):
frappe.throw(_("Can not filter based on Account, if grouped by Account"))
- if (filters.get("voucher_no") and filters.get("group_by") == 'Group by Voucher'):
+ if (filters.get("voucher_no")
+ and filters.get("group_by") in [_('Group by Voucher'), _('Group by Voucher (Consolidated)')]):
frappe.throw(_("Can not filter based on Voucher No, if grouped by Voucher"))
if filters.from_date > filters.to_date:
@@ -114,30 +117,37 @@
return result
-
def get_gl_entries(filters):
currency_map = get_currency(filters)
- select_fields = """, debit_in_account_currency,
- credit_in_account_currency""" \
+ select_fields = """, debit, credit, debit_in_account_currency,
+ credit_in_account_currency """
- order_by_fields = "posting_date, account"
- if filters.get("group_by") == "Group by Voucher":
- order_by_fields = "posting_date, voucher_type, voucher_no"
+ group_by_statement = ''
+ order_by_statement = "order by posting_date, account"
+
+ if filters.get("group_by") == _("Group by Voucher"):
+ order_by_statement = "order by posting_date, voucher_type, voucher_no"
+
+ if filters.get("group_by") == _("Group by Voucher (Consolidated)"):
+ group_by_statement = "group by voucher_type, voucher_no, account, cost_center"
+ select_fields = """, sum(debit) as debit, sum(credit) as credit,
+ sum(debit_in_account_currency) as debit_in_account_currency,
+ sum(credit_in_account_currency) as credit_in_account_currency"""
gl_entries = frappe.db.sql(
"""
select
posting_date, account, party_type, party,
- debit, credit,
voucher_type, voucher_no, cost_center, project,
against_voucher_type, against_voucher, account_currency,
remarks, against, is_opening {select_fields}
from `tabGL Entry`
- where company=%(company)s {conditions}
- order by {order_by_fields}
+ where company=%(company)s {conditions} {group_by_statement}
+ {order_by_statement}
""".format(
select_fields=select_fields, conditions=get_conditions(filters),
- order_by_fields=order_by_fields
+ group_by_statement=group_by_statement,
+ order_by_statement=order_by_statement
),
filters, as_dict=1)
@@ -204,13 +214,13 @@
# Opening for filtered account
data.append(totals.opening)
- if filters.get("group_by"):
+ if filters.get("group_by") != _('Group by Voucher (Consolidated)'):
for acc, acc_dict in iteritems(gle_map):
# acc
if acc_dict.entries:
# opening
data.append({})
- if filters.get("group_by") != "Group by Voucher":
+ if filters.get("group_by") != _("Group by Voucher"):
data.append(acc_dict.totals.opening)
data += acc_dict.entries
@@ -219,10 +229,9 @@
data.append(acc_dict.totals.total)
# closing
- if filters.get("group_by") != "Group by Voucher":
+ if filters.get("group_by") != _("Group by Voucher"):
data.append(acc_dict.totals.closing)
data.append({})
-
else:
data += entries
@@ -234,7 +243,6 @@
return data
-
def get_totals_dict():
def _get_debit_credit_dict(label):
return _dict(
@@ -251,12 +259,12 @@
)
def group_by_field(group_by):
- if group_by == 'Group by Party':
+ if group_by == _('Group by Party'):
return 'party'
- elif group_by == 'Group by Voucher':
- return 'voucher_no'
- else:
+ elif group_by in [_('Group by Voucher (Consolidated)'), _('Group by Account')]:
return 'account'
+ else:
+ return 'voucher_no'
def initialize_gle_map(gl_entries, filters):
gle_map = frappe._dict()
@@ -291,7 +299,7 @@
elif gle.posting_date <= to_date:
update_value_in_dict(gle_map[gle.get(group_by)].totals, 'total', gle)
update_value_in_dict(totals, 'total', gle)
- if filters.get("group_by"):
+ if filters.get("group_by") != _('Group by Voucher (Consolidated)'):
gle_map[gle.get(group_by)].entries.append(gle)
else:
entries.append(gle)
@@ -301,7 +309,6 @@
return totals, entries
-
def get_result_as_list(data, filters):
balance, balance_in_account_currency = 0, 0
inv_details = get_supplier_invoice_details()
diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js
index 29909f8..a6e6974 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js
+++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js
@@ -13,7 +13,11 @@
}
});
},
-
+ onload: function(frm) {
+ if(frm.is_new() && frm.doc.asset) {
+ frm.trigger("set_current_asset_value");
+ }
+ },
asset: function(frm) {
frm.trigger("set_current_asset_value");
},
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 601af69..c6f4fcc 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
@@ -184,39 +185,6 @@
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "No",
- "fieldname": "is_subcontracted",
- "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": "Supply Raw Materials",
- "length": 0,
- "no_copy": 0,
- "options": "No\nYes",
- "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_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
@@ -253,40 +221,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "eval:doc.is_subcontracted==\"Yes\"",
- "fieldname": "supplier_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": "Supplier 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
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"description": "",
"fieldname": "company",
"fieldtype": "Link",
@@ -1360,6 +1294,168 @@
"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
+ },
+ {
+ "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 Target 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
+ },
+ {
+ "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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "No",
+ "fieldname": "is_subcontracted",
+ "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": "Supply Raw Materials",
+ "length": 0,
+ "no_copy": 0,
+ "options": "No\nYes",
+ "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_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.is_subcontracted==\"Yes\"",
+ "fieldname": "supplier_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": "Supplier 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
+ },
+ {
+ "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,
@@ -1387,6 +1483,38 @@
"unique": 0
},
{
+ "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
+ },
+ {
"allow_bulk_edit": 1,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
@@ -1426,6 +1554,74 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "collapsible_depends_on": "supplied_items",
+ "columns": 0,
+ "fieldname": "raw_material_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": "Raw Materials Supplied",
+ "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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "supplied_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": "Supplied Items",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "po_raw_material_details",
+ "oldfieldtype": "Table",
+ "options": "Purchase Order Item Supplied",
+ "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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"columns": 0,
"fieldname": "sb_last_purchase",
"fieldtype": "Section Break",
@@ -3434,107 +3630,6 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "collapsible_depends_on": "supplied_items",
- "columns": 0,
- "fieldname": "raw_material_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": "Raw Materials Supplied",
- "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
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.is_subcontracted",
- "fieldname": "supplied_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": "Supplied Items",
- "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
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fieldname": "supplied_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": "Supplied Items",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "po_raw_material_details",
- "oldfieldtype": "Table",
- "options": "Purchase Order Item Supplied",
- "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
- },
- {
- "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",
@@ -3736,8 +3831,8 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-08-29 12:16:12.886021",
- "modified_by": "nabinhait@gmail.com",
+ "modified": "2018-11-12 19:59:49.211145",
+ "modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
"owner": "Administrator",
diff --git a/erpnext/buying/report/purchase_analytics/__init__.py b/erpnext/buying/report/purchase_analytics/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/buying/report/purchase_analytics/__init__.py
diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.js b/erpnext/buying/report/purchase_analytics/purchase_analytics.js
new file mode 100644
index 0000000..297ec51
--- /dev/null
+++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.js
@@ -0,0 +1,128 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Purchase Analytics"] = {
+ "filters": [
+ {
+ fieldname: "tree_type",
+ label: __("Tree Type"),
+ fieldtype: "Select",
+ options: ["Supplier Group","Supplier","Item Group","Item"],
+ default: "Supplier",
+ reqd: 1
+ },
+ {
+ fieldname: "doc_type",
+ label: __("based_on"),
+ fieldtype: "Select",
+ options: ["Purchase Order","Purchase Receipt","Purchase Invoice"],
+ default: "Purchase Invoice",
+ reqd: 1
+ },
+ {
+ fieldname: "value_quantity",
+ label: __("Value Or Qty"),
+ fieldtype: "Select",
+ options: [
+ { "value": "Value", "label": __("Value") },
+ { "value": "Quantity", "label": __("Quantity") },
+ ],
+ default: "Value",
+ reqd: 1
+ },
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ default: frappe.defaults.get_user_default("year_start_date"),
+ reqd: 1
+ },
+ {
+ fieldname:"to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ default: frappe.defaults.get_user_default("year_end_date"),
+ reqd: 1
+ },
+ {
+ fieldname: "company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company"),
+ reqd: 1
+ },
+ {
+ fieldname: "range",
+ label: __("Range"),
+ fieldtype: "Select",
+ options: [
+ { "value": "Weekly", "label": __("Weekly") },
+ { "value": "Monthly", "label": __("Monthly") },
+ { "value": "Quarterly", "label": __("Quarterly") },
+ { "value": "Yearly", "label": __("Yearly") }
+ ],
+ default: "Monthly",
+ reqd: 1
+ }
+
+ ],
+ "formatter": function(value, row, column, data) {
+ if(!value){
+ value = 0
+ }
+ return value;
+ },
+ get_datatable_options(options) {
+ return Object.assign(options, {
+ checkboxColumn: true,
+ events: {
+ onCheckRow: function(data) {
+ row_name = data[2].content;
+ row_values = data.slice(5).map(function (column) {
+ return column.content;
+ })
+
+ entry = {
+ 'name':row_name,
+ 'values':row_values
+ }
+
+ let raw_data = frappe.query_report.chart.data;
+ let new_datasets = raw_data.datasets;
+
+ var found = false;
+
+ for(var i=0; i < new_datasets.length;i++){
+ if(new_datasets[i].name == row_name){
+ found = true;
+ new_datasets.splice(i,1);
+ break;
+ }
+ }
+
+ if(!found){
+ new_datasets.push(entry);
+ }
+
+ let new_data = {
+ labels: raw_data.labels,
+ datasets: new_datasets
+ }
+
+ setTimeout(() => {
+ frappe.query_report.chart.update(new_data)
+ },200)
+
+
+ setTimeout(() => {
+ frappe.query_report.chart.draw(true);
+ }, 800)
+
+ frappe.query_report.raw_chart_data = new_data;
+ },
+ }
+ })
+ },
+}
diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.json b/erpnext/buying/report/purchase_analytics/purchase_analytics.json
new file mode 100644
index 0000000..996e3ee
--- /dev/null
+++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.json
@@ -0,0 +1,26 @@
+{
+ "add_total_row": 0,
+ "creation": "2018-10-05 16:08:24.156448",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2018-10-05 16:08:33.272201",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Analytics",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Purchase Order",
+ "report_name": "Purchase Analytics",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Purchase Manager"
+ },
+ {
+ "role": "Purchase User"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.py b/erpnext/buying/report/purchase_analytics/purchase_analytics.py
new file mode 100644
index 0000000..0f94947
--- /dev/null
+++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from erpnext.selling.report.sales_analytics.sales_analytics import Analytics
+
+def execute(filters=None):
+ return Analytics(filters).run()
diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py
index 270519e..e99b1d8 100644
--- a/erpnext/config/buying.py
+++ b/erpnext/config/buying.py
@@ -122,10 +122,10 @@
"icon": "fa fa-table",
"items": [
{
- "type": "page",
- "name": "purchase-analytics",
- "label": _("Purchase Analytics"),
- "icon": "fa fa-bar-chart",
+ "type": "report",
+ "is_query_report": True,
+ "name": "Purchase Analytics",
+ "doctype": "Purchase Order"
},
{
"type": "report",
diff --git a/erpnext/config/manufacturing.py b/erpnext/config/manufacturing.py
index 16ca914..621c2dc 100644
--- a/erpnext/config/manufacturing.py
+++ b/erpnext/config/manufacturing.py
@@ -112,11 +112,12 @@
"is_query_report": True,
"name": "Completed Work Orders",
"doctype": "Work Order"
- },{
- "type": "page",
- "name": "production-analytics",
- "label": _("Production Analytics"),
- "icon": "fa fa-bar-chart",
+ },
+ {
+ "type": "report",
+ "is_query_report": True,
+ "name": "Production Analytics",
+ "doctype": "Work Order"
},
{
"type": "report",
diff --git a/erpnext/config/selling.py b/erpnext/config/selling.py
index 029fdac..94f3102 100644
--- a/erpnext/config/selling.py
+++ b/erpnext/config/selling.py
@@ -185,10 +185,10 @@
"icon": "fa fa-table",
"items": [
{
- "type": "page",
- "name": "sales-analytics",
- "label": _("Sales Analytics"),
- "icon": "fa fa-bar-chart",
+ "type": "report",
+ "is_query_report": True,
+ "name": "Sales Analytics",
+ "doctype": "Sales Order"
},
{
"type": "page",
diff --git a/erpnext/config/setup.py b/erpnext/config/setup.py
index e38b30d..1a119e8 100644
--- a/erpnext/config/setup.py
+++ b/erpnext/config/setup.py
@@ -64,7 +64,7 @@
{
"type": "help",
"label": _("Users and Permissions"),
- "youtube_id": "fnBoRhBrwR4"
+ "youtube_id": "8Slw1hsTmUI"
},
{
"type": "help",
diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py
index abdca0d..60eee71 100644
--- a/erpnext/config/stock.py
+++ b/erpnext/config/stock.py
@@ -218,10 +218,10 @@
"doctype": "Item Price",
},
{
- "type": "page",
- "name": "stock-analytics",
- "label": _("Stock Analytics"),
- "icon": "fa fa-bar-chart"
+ "type": "report",
+ "is_query_report": True,
+ "name": "Stock Analytics",
+ "doctype": "Stock Entry"
},
{
"type": "report",
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 87b7942..f48b48b 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -12,7 +12,7 @@
source_link = "https://github.com/frappe/erpnext"
develop_version = '12.x.x-develop'
-staging_version = '11.0.3-beta.20'
+staging_version = '11.0.3-beta.21'
error_report_email = "support@erpnext.com"
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index e74a375..dbbf3d3 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -104,13 +104,26 @@
}
});
},
-
+
get_items_for_mr: function(frm) {
frappe.call({
- method: "get_items_for_material_requests",
+ method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_items_for_material_requests",
freeze: true,
- doc: frm.doc,
- callback: function() {
+ args: {doc: frm.doc},
+ callback: function(r) {
+ if(r.message) {
+ frm.set_value('mr_items', []);
+ $.each(r.message, function(i, d) {
+ var item = frm.add_child('mr_items');
+ item.actual_qty = d.actual_qty;
+ item.item_code = d.item_code;
+ item.item_name = d.item_name;
+ item.min_order_qty = d.min_order_qty;
+ item.quantity = d.quantity;
+ item.sales_order = d.sales_order;
+ item.warehouse = d.warehouse;
+ });
+ }
refresh_field('mr_items');
}
});
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 12f2f04..7d11ae4 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -10,6 +10,7 @@
from frappe.utils import cstr, flt, cint, nowdate, add_days, comma_and, now_datetime
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
from six import string_types
+from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
class ProductionPlan(Document):
def validate(self):
@@ -209,9 +210,10 @@
def set_status(self):
self.status = {
- '0': 'Draft',
- '1': 'Submitted'
- }[cstr(self.docstatus or 0)]
+ 0: 'Draft',
+ 1: 'Submitted',
+ 2: 'Cancelled'
+ }.get(self.docstatus)
if self.total_produced_qty > 0:
self.status = "In Process"
@@ -281,102 +283,6 @@
return item_dict
- def get_items_for_material_requests(self):
- self.mr_items = []
-
- for data in self.po_items:
- bom_wise_item_details = {}
- if not data.planned_qty:
- frappe.throw(_("For row {0}: Enter planned qty").format(data.idx))
-
- if data.include_exploded_items and data.bom_no and self.include_subcontracted_items:
- for d in frappe.db.sql("""select bei.item_code, item.default_bom as bom,
- ifnull(sum(bei.stock_qty/ifnull(bom.quantity, 1)), 0) as qty, item.item_name,
- bei.description, bei.stock_uom, item.min_order_qty, bei.source_warehouse,
- item.default_material_request_type, item.min_order_qty, item_default.default_warehouse
- from
- `tabBOM Explosion Item` bei
- JOIN `tabBOM` bom ON bom.name = bei.parent
- JOIN `tabItem` item ON item.name = bei.item_code
- LEFT JOIN `tabItem Default` item_default
- ON item_default.parent = item.name and item_default.company=%s
- where
- bei.docstatus < 2
- and bom.name=%s and item.is_stock_item in (1, {0})
- group by bei.item_code, bei.stock_uom""".format(0 if self.include_non_stock_items else 1),
- (self.company, data.bom_no), as_dict=1):
- bom_wise_item_details.setdefault(d.item_code, d)
- else:
- bom_wise_item_details = self.get_subitems(data, bom_wise_item_details, data.bom_no, 1)
-
- for item, item_details in bom_wise_item_details.items():
- if item_details.qty > 0:
- self.add_item_in_material_request_items(item, item_details, data)
-
- def get_subitems(self, data, bom_wise_item_details, bom_no, parent_qty):
- items = frappe.db.sql("""
- SELECT
- bom_item.item_code, default_material_request_type, item.item_name,
- ifnull(%(parent_qty)s * sum(bom_item.stock_qty/ifnull(bom.quantity, 1)), 0) as qty,
- item.is_sub_contracted_item as is_sub_contracted, bom_item.source_warehouse,
- item.default_bom as default_bom, bom_item.description as description,
- bom_item.stock_uom as stock_uom, item.min_order_qty as min_order_qty,
- item_default.default_warehouse
- FROM
- `tabBOM Item` bom_item
- JOIN `tabBOM` bom ON bom.name = bom_item.parent
- JOIN tabItem item ON bom_item.item_code = item.name
- LEFT JOIN `tabItem Default` item_default
- ON item.name = item_default.parent and item_default.company = %(company)s
- where
- bom.name = %(bom)s
- and bom_item.docstatus < 2
- and item.is_stock_item in (1, {0})
- group by bom_item.item_code""".format(0 if self.include_non_stock_items else 1),{
- 'bom': bom_no,
- 'parent_qty': parent_qty,
- 'company': self.company
- }, as_dict=1)
-
- for d in items:
- if not data.include_exploded_items or not d.default_bom:
- if d.item_code in bom_wise_item_details:
- bom_wise_item_details[d.item_code].qty = bom_wise_item_details[d.item_code].qty + d.qty
- else:
- bom_wise_item_details[d.item_code] = d
-
- if data.include_exploded_items and d.default_bom:
- if ((d.default_material_request_type in ["Manufacture", "Purchase"] and
- not d.is_sub_contracted) or (d.is_sub_contracted and self.include_subcontracted_items)):
- if d.qty > 0:
- self.get_subitems(data, bom_wise_item_details, d.default_bom, d.qty)
-
- return bom_wise_item_details
-
- def add_item_in_material_request_items(self, item, row, data):
- total_qty = row.qty * data.planned_qty
- projected_qty, actual_qty = get_bin_details(row)
-
- requested_qty = 0
- if self.ignore_existing_ordered_qty:
- requested_qty = total_qty
- else:
- requested_qty = total_qty - projected_qty
-
- if requested_qty > 0 and requested_qty < row.min_order_qty:
- requested_qty = row.min_order_qty
-
- if requested_qty > 0:
- self.append('mr_items', {
- 'item_code': item,
- 'item_name': row.item_name,
- 'quantity': requested_qty,
- 'warehouse': row.source_warehouse or row.default_warehouse,
- 'actual_qty': actual_qty,
- 'min_order_qty': row.min_order_qty,
- 'sales_order': data.sales_order
- })
-
def make_work_order(self):
wo_list = []
self.validate_data()
@@ -466,6 +372,87 @@
else :
msgprint(_("No material request created"))
+def get_exploded_items(bom_wise_item_details, company, bom_no, include_non_stock_items):
+ for d in frappe.db.sql("""select bei.item_code, item.default_bom as bom,
+ ifnull(sum(bei.stock_qty/ifnull(bom.quantity, 1)), 0) as qty, item.item_name,
+ bei.description, bei.stock_uom, item.min_order_qty, bei.source_warehouse,
+ item.default_material_request_type, item.min_order_qty, item_default.default_warehouse
+ from
+ `tabBOM Explosion Item` bei
+ JOIN `tabBOM` bom ON bom.name = bei.parent
+ JOIN `tabItem` item ON item.name = bei.item_code
+ LEFT JOIN `tabItem Default` item_default
+ ON item_default.parent = item.name and item_default.company=%s
+ where
+ bei.docstatus < 2
+ and bom.name=%s and item.is_stock_item in (1, {0})
+ group by bei.item_code, bei.stock_uom""".format(0 if include_non_stock_items else 1),
+ (company, bom_no), as_dict=1):
+ bom_wise_item_details.setdefault(d.get('item_code'), d)
+ return bom_wise_item_details
+
+def get_subitems(doc, data, bom_wise_item_details, bom_no, company, include_non_stock_items, include_subcontracted_items, parent_qty):
+ items = frappe.db.sql("""
+ SELECT
+ bom_item.item_code, default_material_request_type, item.item_name,
+ ifnull(%(parent_qty)s * sum(bom_item.stock_qty/ifnull(bom.quantity, 1)), 0) as qty,
+ item.is_sub_contracted_item as is_sub_contracted, bom_item.source_warehouse,
+ item.default_bom as default_bom, bom_item.description as description,
+ bom_item.stock_uom as stock_uom, item.min_order_qty as min_order_qty,
+ item_default.default_warehouse
+ FROM
+ `tabBOM Item` bom_item
+ JOIN `tabBOM` bom ON bom.name = bom_item.parent
+ JOIN tabItem item ON bom_item.item_code = item.name
+ LEFT JOIN `tabItem Default` item_default
+ ON item.name = item_default.parent and item_default.company = %(company)s
+ where
+ bom.name = %(bom)s
+ and bom_item.docstatus < 2
+ and item.is_stock_item in (1, {0})
+ group by bom_item.item_code""".format(0 if include_non_stock_items else 1),{
+ 'bom': bom_no,
+ 'parent_qty': parent_qty,
+ 'company': company
+ }, as_dict=1)
+
+ for d in items:
+ if not data.get('include_exploded_items') or not d.default_bom:
+ if d.item_code in bom_wise_item_details:
+ bom_wise_item_details[d.item_code].qty = bom_wise_item_details[d.item_code].qty + d.qty
+ else:
+ bom_wise_item_details[d.item_code] = d
+
+ if data.get('include_exploded_items') and d.default_bom:
+ if ((d.default_material_request_type in ["Manufacture", "Purchase"] and
+ not d.is_sub_contracted) or (d.is_sub_contracted and include_subcontracted_items)):
+ if d.qty > 0:
+ get_subitems(doc, data, bom_wise_item_details, d.default_bom, company, include_non_stock_items, include_subcontracted_items, d.qty)
+ return bom_wise_item_details
+
+def add_item_in_material_request_items(doc, planned_qty, ignore_existing_ordered_qty, item, row, data, warehouse, company):
+ total_qty = row.qty * planned_qty
+ projected_qty, actual_qty = get_bin_details(row)
+
+ requested_qty = 0
+ if ignore_existing_ordered_qty:
+ requested_qty = total_qty
+ else:
+ requested_qty = total_qty - projected_qty
+ if requested_qty > 0 and requested_qty < row.min_order_qty:
+ requested_qty = row.min_order_qty
+ item_group_defaults = get_item_group_defaults(item, company)
+ if requested_qty > 0:
+ doc.setdefault('mr_items', []).append({
+ 'item_code': item,
+ 'item_name': row.item_name,
+ 'quantity': requested_qty,
+ 'warehouse': warehouse or row.source_warehouse or row.default_warehouse or item_group_defaults.get("default_warehouse"),
+ 'actual_qty': actual_qty,
+ 'min_order_qty': row.min_order_qty,
+ 'sales_order': data.get('sales_order')
+ })
+
def get_sales_orders(self):
so_filter = item_filter = ""
if self.from_date:
@@ -520,3 +507,46 @@
""".format(conditions=conditions), { "item_code": row.item_code }, as_list=1)
return item_projected_qty and item_projected_qty[0] or (0,0)
+
+@frappe.whitelist()
+def get_items_for_material_requests(doc, company=None):
+ if isinstance(doc, string_types):
+ doc = frappe._dict(json.loads(doc))
+
+ doc['mr_items'] = []
+ po_items = doc['po_items'] if doc.get('po_items') else doc['items']
+
+ for data in po_items:
+ warehouse = None
+ bom_wise_item_details = {}
+
+ if data.get('required_qty'):
+ planned_qty = data.get('required_qty')
+ bom_no = data.get('bom')
+ ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty')
+ include_non_stock_items = 1
+ warehouse = data.get('for_warehouse')
+ if data.get('include_exploded_items'):
+ include_subcontracted_items = 1
+ else:
+ include_subcontracted_items = 0
+ else:
+ planned_qty = data.get('planned_qty')
+ bom_no = data.get('bom_no')
+ include_subcontracted_items = doc['include_subcontracted_items']
+ company = doc['company']
+ include_non_stock_items = doc['include_non_stock_items']
+ ignore_existing_ordered_qty = doc['ignore_existing_ordered_qty']
+ if not planned_qty:
+ frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get('idx')))
+
+ if data.get('include_exploded_items') and bom_no and include_subcontracted_items:
+ # fetch exploded items from BOM
+ bom_wise_item_details = get_exploded_items(bom_wise_item_details, company, bom_no, include_non_stock_items)
+ else:
+ bom_wise_item_details = get_subitems(doc, data, bom_wise_item_details, bom_no, company, include_non_stock_items, include_subcontracted_items, 1)
+ for item, item_details in bom_wise_item_details.items():
+ if item_details.qty > 0:
+ add_item_in_material_request_items(doc, planned_qty, ignore_existing_ordered_qty, item, item_details, data, warehouse, company)
+
+ return doc['mr_items']
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 7cf4268..a33d42b 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -10,6 +10,7 @@
from erpnext.manufacturing.doctype.production_plan.production_plan import get_sales_orders
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests
class TestProductionPlan(unittest.TestCase):
def setUp(self):
@@ -160,7 +161,9 @@
'planned_start_date': args.planned_start_date or now_datetime()
}]
})
- pln.get_items_for_material_requests()
+ mr_items = get_items_for_material_requests(pln.as_dict())
+ for d in mr_items:
+ pln.append('mr_items', d)
if not args.do_not_save:
pln.insert()
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 1d465d5..e73328f 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -448,7 +448,9 @@
if item_dict.get(d.item_code):
d.required_qty = item_dict.get(d.item_code).get("qty")
else:
- for item in sorted(item_dict.values(), key=lambda d: d['idx']):
+ # Attribute a big number (999) to idx for sorting putpose in case idx is NULL
+ # For instance in BOM Explosion Item child table, the items coming from sub assembly items
+ for item in sorted(item_dict.values(), key=lambda d: d['idx'] or 9999):
self.append('required_items', {
'operation': item.operation,
'item_code': item.item_code,
diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js
index 049a822..2ac6fa0 100644
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js
+++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js
@@ -16,6 +16,22 @@
"fieldname": "show_exploded_view",
"label": __("Show exploded view"),
"fieldtype": "Check"
+ }, {
+ "fieldname": "qty_to_produce",
+ "label": __("Quantity to Produce"),
+ "fieldtype": "Int",
+ "default": "1"
+ },
+ ],
+ "formatter": function(value, row, column, data, default_formatter) {
+ value = default_formatter(value, row, column, data);
+ if (column.id == "Item"){
+ if (data["Enough Parts to Build"] > 0){
+ value = `<a style='color:green' href="#Form/Item/${data['Item']}" data-doctype="Item">${data['Item']}</a>`
+ } else {
+ value = `<a style='color:red' href="#Form/Item/${data['Item']}" data-doctype="Item">${data['Item']}</a>`
+ }
}
- ]
+ return value
+ }
}
diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
index 3236839..ec36720 100644
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
+++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
@@ -7,6 +7,7 @@
def execute(filters=None):
if not filters: filters = {}
+
columns = get_columns()
data = get_bom_stock(filters)
@@ -18,6 +19,7 @@
columns = [
_("Item") + ":Link/Item:150",
_("Description") + "::500",
+ _("Qty per BOM Line") + ":Float:100",
_("Required Qty") + ":Float:100",
_("In Stock Qty") + ":Float:100",
_("Enough Parts to Build") + ":Float:200",
@@ -32,6 +34,10 @@
table = "`tabBOM Item`"
qty_field = "qty"
+ qty_to_produce = filters.get("qty_to_produce", 1)
+ if int(qty_to_produce) <= 0:
+ frappe.throw(_("Quantity to Produce can not be less than Zero"))
+
if filters.get("show_exploded_view"):
table = "`tabBOM Explosion Item`"
qty_field = "stock_qty"
@@ -50,11 +56,12 @@
return frappe.db.sql("""
SELECT
- bom_item.item_code ,
+ bom_item.item_code,
bom_item.description ,
bom_item.{qty_field},
+ bom_item.{qty_field} * {qty_to_produce},
sum(ledger.actual_qty) as actual_qty,
- sum(FLOOR(ledger.actual_qty / bom_item.{qty_field}))as to_build
+ sum(FLOOR(ledger.actual_qty / (bom_item.{qty_field} * {qty_to_produce})))
FROM
{table} AS bom_item
LEFT JOIN `tabBin` AS ledger
@@ -63,4 +70,10 @@
WHERE
bom_item.parent = '{bom}' and bom_item.parenttype='BOM'
- GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom))
+ GROUP BY bom_item.item_code""".format(
+ qty_field=qty_field,
+ table=table,
+ conditions=conditions,
+ bom=bom,
+ qty_to_produce=qty_to_produce or 1)
+ )
diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py
index 0602193..7447a1f 100644
--- a/erpnext/manufacturing/report/production_analytics/production_analytics.py
+++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py
@@ -3,17 +3,16 @@
from __future__ import unicode_literals
import frappe
-from frappe import _
+from frappe import _, scrub
from frappe.utils import getdate
-from erpnext.selling.report.sales_analytics.sales_analytics import (get_period_date_ranges,get_period)
+from erpnext.stock.report.stock_analytics.stock_analytics import (get_period_date_ranges, get_period)
def execute(filters=None):
columns = get_columns(filters)
- data, chart = get_data(filters,columns)
- return columns, data,None ,chart
+ data, chart = get_data(filters, columns)
+ return columns, data, None , chart
def get_columns(filters):
-
columns =[
{
"label": _("Status"),
@@ -22,122 +21,113 @@
"width": 140
}]
- ranges = get_period_date_ranges(period=filters["range"], year_start_date = filters["from_date"],year_end_date=filters["to_date"])
+ ranges = get_period_date_ranges(filters)
for dummy, end_date in ranges:
- label = field_name = get_period(end_date,filters["range"])
+ period = get_period(end_date, filters)
- columns.append(
- {
- "label": _(label),
- "fieldname": field_name,
+ columns.append({
+ "label": _(period),
+ "fieldname": scrub(period),
"fieldtype": "Float",
"width": 120
- },
- )
+ })
return columns
-def get_data_list(filters,entry):
-
- data_list = {
- "All Work Orders" : {},
- "Not Started" : {},
- "Overdue" : {},
- "Pending" : {},
- "Completed" : {}
+def get_periodic_data(filters, entry):
+ periodic_data = {
+ "All Work Orders": {},
+ "Not Started": {},
+ "Overdue": {},
+ "Pending": {},
+ "Completed": {}
}
- ranges = get_period_date_ranges(period=filters["range"], year_start_date = filters["from_date"],year_end_date=filters["to_date"])
+ ranges = get_period_date_ranges(filters)
- for from_date,end_date in ranges:
- period = get_period(end_date,filters["range"])
+ for from_date, end_date in ranges:
+ period = get_period(end_date, filters)
for d in entry:
if getdate(d.creation) <= getdate(from_date) or getdate(d.creation) <= getdate(end_date) :
- data_list = update_data_list(data_list,"All Work Orders",period)
-
+ periodic_data = update_periodic_data(periodic_data, "All Work Orders", period)
if d.status == 'Completed':
if getdate(d.actual_end_date) < getdate(from_date) or getdate(d.modified) < getdate(from_date):
- data_list = update_data_list(data_list, "Completed",period)
+ periodic_data = update_periodic_data(periodic_data, "Completed", period)
elif getdate(d.actual_start_date) < getdate(from_date) :
- data_list = update_data_list(data_list, "Pending", period)
+ periodic_data = update_periodic_data(periodic_data, "Pending", period)
elif getdate(d.planned_start_date) < getdate(from_date) :
- data_list = update_data_list(data_list, "Overdue", period)
+ periodic_data = update_periodic_data(periodic_data, "Overdue", period)
else:
- data_list = update_data_list(data_list, "Not Started", period)
+ periodic_data = update_periodic_data(periodic_data, "Not Started", period)
elif d.status == 'In Process':
if getdate(d.actual_start_date) < getdate(from_date) :
- data_list = update_data_list(data_list, "Pending", period)
+ periodic_data = update_periodic_data(periodic_data, "Pending", period)
elif getdate(d.planned_start_date) < getdate(from_date) :
- data_list = update_data_list(data_list, "Overdue", period)
+ periodic_data = update_periodic_data(periodic_data, "Overdue", period)
else:
- data_list = update_data_list(data_list, "Not Started", period)
+ periodic_data = update_periodic_data(periodic_data, "Not Started", period)
elif d.status == 'Not Started':
if getdate(d.planned_start_date) < getdate(from_date) :
- data_list = update_data_list(data_list, "Overdue", period)
+ periodic_data = update_periodic_data(periodic_data, "Overdue", period)
else:
- data_list = update_data_list(data_list, "Not Started", period)
- return data_list
+ periodic_data = update_periodic_data(periodic_data, "Not Started", period)
+ return periodic_data
-def update_data_list(data_list, status, period):
- if data_list.get(status).get(period):
- data_list[status][period] += 1
+def update_periodic_data(periodic_data, status, period):
+ if periodic_data.get(status).get(period):
+ periodic_data[status][period] += 1
else:
- data_list[status][period] = 1
+ periodic_data[status][period] = 1
- return data_list
+ return periodic_data
-def get_data(filters,columns):
-
+def get_data(filters, columns):
data = []
-
entry = frappe.get_all("Work Order",
fields=["creation", "modified", "actual_start_date", "actual_end_date", "planned_start_date", "planned_end_date", "status"],
- filters={"docstatus" : 1, "company" : filters["company"] })
+ filters={"docstatus": 1, "company": filters["company"] })
- data_list = get_data_list(filters,entry)
+ periodic_data = get_periodic_data(filters,entry)
labels = ["All Work Orders", "Not Started", "Overdue", "Pending", "Completed"]
-
- chart_data = get_chart_data(data_list,columns)
-
- ranges = get_period_date_ranges(period=filters["range"], year_start_date = filters["from_date"],year_end_date=filters["to_date"])
+ chart_data = get_chart_data(periodic_data,columns)
+ ranges = get_period_date_ranges(filters)
for label in labels:
work = {}
work["Status"] = label
for dummy,end_date in ranges:
- period = get_period(end_date,filters["range"])
- if data_list.get(label).get(period):
- work[period] = data_list.get(label).get(period)
+ period = get_period(end_date, filters)
+ if periodic_data.get(label).get(period):
+ work[scrub(period)] = periodic_data.get(label).get(period)
else:
- work[period] = 0.0
+ work[scrub(period)] = 0.0
data.append(work)
return data, chart_data
-def get_chart_data(data_list,columns):
-
+def get_chart_data(periodic_data, columns):
labels = [d.get("label") for d in columns[1:]]
all_data, not_start, overdue, pending, completed = [], [], [] , [], []
datasets = []
for d in labels:
- all_data.append(data_list.get("All Work Orders").get(d))
- not_start.append(data_list.get("Not Started").get(d))
- overdue.append(data_list.get("Overdue").get(d))
- pending.append(data_list.get("Pending").get(d))
- completed.append(data_list.get("Completed").get(d))
+ all_data.append(periodic_data.get("All Work Orders").get(d))
+ not_start.append(periodic_data.get("Not Started").get(d))
+ overdue.append(periodic_data.get("Overdue").get(d))
+ pending.append(periodic_data.get("Pending").get(d))
+ completed.append(periodic_data.get("Completed").get(d))
datasets.append({'name':'All Work Orders', 'values': all_data})
datasets.append({'name':'Not Started', 'values': not_start})
@@ -148,10 +138,9 @@
chart = {
"data": {
'labels': labels,
- 'datasets':datasets
+ 'datasets': datasets
}
}
-
chart["type"] = "line"
return chart
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
old mode 100644
new mode 100755
index 9c56948..ca25667
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -568,7 +568,13 @@
erpnext.patches.v11_0.add_default_dispatch_notification_template
erpnext.patches.v11_0.add_market_segments
erpnext.patches.v11_0.add_sales_stages
+execute:frappe.delete_doc("Page", "Sales Analytics")
+execute:frappe.delete_doc("Page", "Purchase Analytics")
+execute:frappe.delete_doc("Page", "Stock Analytics")
+execute:frappe.delete_doc("Page", "Production Analytics")
erpnext.patches.v11_0.ewaybill_fields_gst_india
erpnext.patches.v11_0.drop_column_max_days_allowed
erpnext.patches.v11_0.change_healthcare_desktop_icons
erpnext.patches.v10_0.update_user_image_in_employee
+erpnext.patches.v11_0.update_delivery_trip_status
+erpnext.patches.v10_0.repost_gle_for_purchase_receipts_with_rejected_items
diff --git a/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py b/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py
new file mode 100644
index 0000000..68c06ef
--- /dev/null
+++ b/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py
@@ -0,0 +1,32 @@
+# Copyright (c) 2017, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe, erpnext
+
+def execute():
+ for company in frappe.get_all("Company"):
+ if not erpnext.is_perpetual_inventory_enabled(company.name):
+ continue
+
+ acc_frozen_upto = frappe.db.get_value("Accounts Settings", None, "acc_frozen_upto") or "1900-01-01"
+ pr_with_rejected_warehouse = frappe.db.sql("""
+ select pr.name
+ from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item
+ where pr.name = pr_item.parent
+ and pr.posting_date > %s
+ and pr.docstatus=1
+ and pr.company = %s
+ and pr_item.rejected_qty > 0
+ """, (acc_frozen_upto, company.name), as_dict=1)
+
+ for d in pr_with_rejected_warehouse:
+ doc = frappe.get_doc("Purchase Receipt", d.name)
+
+ doc.docstatus = 2
+ doc.make_gl_entries_on_cancel(repost_future_gle=False)
+
+
+ # update gl entries for submit state of PR
+ doc.docstatus = 1
+ doc.make_gl_entries(repost_future_gle=False)
diff --git a/erpnext/patches/v11_0/update_delivery_trip_status.py b/erpnext/patches/v11_0/update_delivery_trip_status.py
new file mode 100755
index 0000000..64b3063
--- /dev/null
+++ b/erpnext/patches/v11_0/update_delivery_trip_status.py
@@ -0,0 +1,27 @@
+# Copyright (c) 2017, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc('stock', 'doctype', 'delivery_trip')
+ frappe.reload_doc('stock', 'doctype', 'delivery_stop', force=True)
+
+ for trip in frappe.get_all("Delivery Trip"):
+ trip_doc = frappe.get_doc("Delivery Trip", trip.name)
+
+ status = {
+ 0: "Draft",
+ 1: "Scheduled",
+ 2: "Cancelled"
+ }[trip_doc.docstatus]
+
+ if trip_doc.docstatus == 1:
+ visited_stops = [stop.visited for stop in trip_doc.delivery_stops]
+ if all(visited_stops):
+ status = "Completed"
+ elif any(visited_stops):
+ status = "In Transit"
+
+ frappe.db.set_value("Delivery Trip", trip.name, "status", status, update_modified=False)
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index 9f1c586..8c84c11 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -128,6 +128,50 @@
settings.ignore_employee_time_overlap = initial_setting
settings.save()
+ def test_timesheet_std_working_hours(self):
+ company = frappe.get_doc('Company', "_Test Company")
+ company.standard_working_hours = 8
+ company.save()
+
+ timesheet = frappe.new_doc("Timesheet")
+ timesheet.employee = "_T-Employee-00001"
+ timesheet.company = '_Test Company'
+ timesheet.append(
+ 'time_logs',
+ {
+ "activity_type": "_Test Activity Type",
+ "from_time": now_datetime(),
+ "to_time": now_datetime() + datetime.timedelta(days= 4)
+ }
+ )
+ timesheet.save()
+
+ ts = frappe.get_doc('Timesheet', timesheet.name)
+ self.assertEqual(ts.total_hours, 32)
+ ts.submit()
+ ts.cancel()
+
+ company = frappe.get_doc('Company', "_Test Company")
+ company.standard_working_hours = 0
+ company.save()
+
+ timesheet = frappe.new_doc("Timesheet")
+ timesheet.employee = "_T-Employee-00001"
+ timesheet.company = '_Test Company'
+ timesheet.append(
+ 'time_logs',
+ {
+ "activity_type": "_Test Activity Type",
+ "from_time": now_datetime(),
+ "to_time": now_datetime() + datetime.timedelta(days= 4)
+ }
+ )
+ timesheet.save()
+
+ ts = frappe.get_doc('Timesheet', timesheet.name)
+ self.assertEqual(ts.total_hours, 96)
+ ts.submit()
+ ts.cancel()
def make_salary_structure_for_timesheet(employee):
salary_structure_name = "Timesheet Salary Structure Test"
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 5234df6..e890bef 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -90,6 +90,13 @@
}
},
+ company: function(frm) {
+ frappe.db.get_value('Company', { 'company_name' : frm.doc.company }, 'standard_working_hours')
+ .then(({ message }) => {
+ (frappe.working_hours = message.standard_working_hours || 0);
+ });
+ },
+
make_invoice: function(frm) {
let dialog = new frappe.ui.Dialog({
title: __("Select Item (optional)"),
@@ -142,11 +149,21 @@
to_time: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
+ var time_diff = (moment(child.to_time).diff(moment(child.from_time),"seconds")) / ( 60 * 60 * 24);
+ var std_working_hours = 0;
if(frm._setting_hours) return;
- frappe.model.set_value(cdt, cdn, "hours", moment(child.to_time).diff(moment(child.from_time),
- "seconds") / 3600);
+
+ var hours = moment(child.to_time).diff(moment(child.from_time), "seconds") / 3600;
+ std_working_hours = time_diff * frappe.working_hours;
+
+ if (std_working_hours < hours && std_working_hours > 0) {
+ frappe.model.set_value(cdt, cdn, "hours", std_working_hours);
+ } else {
+ frappe.model.set_value(cdt, cdn, "hours", hours);
+ }
},
+
time_logs_add: function(frm) {
var $trigger_again = $('.form-grid').find('.grid-row').find('.btn-open-row');
$trigger_again.on('click', () => {
@@ -209,17 +226,23 @@
let d = moment(child.from_time);
if(child.hours) {
- d.add(child.hours, "hours");
- frm._setting_hours = true;
- frappe.model.set_value(cdt, cdn, "to_time",
- d.format(frappe.defaultDatetimeFormat)).then(() => {
- frm._setting_hours = false;
- });
- }
+ var time_diff = (moment(child.to_time).diff(moment(child.from_time),"seconds")) / (60 * 60 * 24);
+ var std_working_hours = 0;
+ var hours = moment(child.to_time).diff(moment(child.from_time), "seconds") / 3600;
+ std_working_hours = time_diff * frappe.working_hours;
- if((frm.doc.__islocal || frm.doc.__onload.maintain_bill_work_hours_same) && child.hours){
- frappe.model.set_value(cdt, cdn, "billing_hours", child.hours);
+ if (std_working_hours < hours && std_working_hours > 0) {
+ frappe.model.set_value(cdt, cdn, "hours", std_working_hours);
+ frappe.model.set_value(cdt, cdn, "to_time", d.add(hours, "hours").format(frappe.defaultDatetimeFormat));
+ } else {
+ d.add(child.hours, "hours");
+ frm._setting_hours = true;
+ frappe.model.set_value(cdt, cdn, "to_time",
+ d.format(frappe.defaultDatetimeFormat)).then(() => {
+ frm._setting_hours = false;
+ });
+ }
}
}
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index f48c0c6..4b466d2 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -9,7 +9,7 @@
import json
from datetime import timedelta
from erpnext.controllers.queries import get_match_cond
-from frappe.utils import flt, time_diff_in_hours, get_datetime, getdate, cint
+from frappe.utils import flt, time_diff_in_hours, get_datetime, getdate, cint, date_diff, add_to_date
from frappe.model.document import Document
from erpnext.manufacturing.doctype.workstation.workstation import (check_if_within_operating_hours,
WorkstationHolidayError)
@@ -27,6 +27,7 @@
self.set_status()
self.validate_dates()
self.validate_time_logs()
+ self.calculate_std_hours()
self.update_cost()
self.calculate_total_amounts()
self.calculate_percentage_billed()
@@ -93,6 +94,17 @@
self.start_date = getdate(start_date)
self.end_date = getdate(end_date)
+ def calculate_std_hours(self):
+ std_working_hours = frappe.get_value("Company", self.company, 'standard_working_hours')
+
+ for time in self.time_logs:
+ if time.from_time and time.to_time:
+ if flt(std_working_hours) > 0:
+ time.hours = flt(std_working_hours) * date_diff(time.to_time, time.from_time)
+ else:
+ if not time.hours:
+ time.hours = time_diff_in_hours(time.to_time, time.from_time)
+
def before_cancel(self):
self.set_status()
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 3e27d56..f3c29fc 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -98,6 +98,15 @@
frm.cscript.calculate_taxes_and_totals();
});
+ frappe.ui.form.on(this.frm.doctype + " Item", {
+ items_add: function(frm, cdt, cdn) {
+ var item = frappe.get_doc(cdt, cdn);
+ if(!item.warehouse && frm.doc.set_warehouse) {
+ item.warehouse = frm.doc.set_warehouse;
+ }
+ }
+ });
+
var me = this;
if(this.frm.fields_dict["items"].grid.get_field('batch_no')) {
this.frm.set_query("batch_no", "items", function(doc, cdt, cdn) {
@@ -253,6 +262,62 @@
this.set_dynamic_labels();
this.setup_sms();
this.setup_quality_inspection();
+ this.frm.fields_dict["scan_barcode"] && this.frm.fields_dict["scan_barcode"].set_value("");
+ this.frm.fields_dict["scan_barcode"] && this.frm.fields_dict["scan_barcode"].set_new_description("");
+ },
+
+ scan_barcode: function() {
+ let scan_barcode_field = this.frm.fields_dict["scan_barcode"];
+
+ let show_description = function(idx, item_code, exist=null) {
+ if(exist) {
+ scan_barcode_field.set_new_description(__('Row : ') + idx + ' ' +
+ item_code + __(' Qty increased by 1'));
+ } else {
+ scan_barcode_field.set_new_description(__('New row : ') + idx + ' ' +
+ item_code + __(' Created'));
+ }
+ }
+
+ if(this.frm.doc.scan_barcode) {
+ frappe.call({
+ method: "erpnext.selling.page.point_of_sale.point_of_sale.search_serial_or_batch_or_barcode_number",
+ args: { search_value: this.frm.doc.scan_barcode }
+ }).then(r => {
+
+ if(r && r.message && r.message.item_code) {
+ let child = "";
+ let add_row_index = -1;
+ let cur_grid= this.frm.fields_dict["items"].grid;
+
+ this.frm.doc.items.map(d => {
+ if(d.item_code==r.message.item_code){
+ add_row_index = d.idx;
+ return;
+ } else if(!d.item_code && add_row_index==-1) {
+ add_row_index = d.idx;
+ }
+ });
+
+ if(add_row_index == -1) {
+ child = frappe.model.add_child(this.frm.doc, cur_grid.doctype, "items", add_row_index);
+ } else {
+ child = cur_grid.get_grid_row(add_row_index-1).doc;
+ }
+ show_description(child.idx, r.message.item_code, child.item_code);
+
+ frappe.model.set_value(child.doctype, child.name, {
+ "item_code": r.message.item_code,
+ "qty": (child.qty || 0) + 1
+ });
+ }
+ else{
+ scan_barcode_field.set_new_description(this.frm.doc.scan_barcode +__(' does not exist!'));
+ }
+ });
+ scan_barcode_field.set_value("");
+ }
+ return false;
},
apply_default_taxes: function() {
@@ -1407,6 +1472,15 @@
}
})
}
+ },
+
+ set_warehouse: function() {
+ var me = this;
+ if(this.frm.doc.set_warehouse) {
+ $.each(this.frm.doc.items || [], function(i, item) {
+ frappe.model.set_value(me.frm.doctype + " Item", item.name, "warehouse", me.frm.doc.set_warehouse);
+ });
+ }
}
});
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 5275e2e..54d7654 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -150,6 +150,8 @@
&& flt(doc.per_delivered, 6) < 100) {
this.frm.add_custom_button(__('Material Request'),
function() { me.make_material_request() }, __("Make"));
+ this.frm.add_custom_button(__('Request for Raw Materials'),
+ function() { me.make_raw_material_request() }, __("Make"));
}
// make purchase order
@@ -313,6 +315,86 @@
})
},
+ make_raw_material_request: function() {
+ var me = this;
+ this.frm.call({
+ doc: this.frm.doc,
+ method: 'get_work_order_items',
+ args: {
+ for_raw_material_request: 1
+ },
+ callback: function(r) {
+ if(!r.message) {
+ frappe.msgprint({
+ message: __('No Items with Bill of Materials.'),
+ indicator: 'orange'
+ });
+ return;
+ }
+ else {
+ me.make_raw_material_request_dialog(r);
+ }
+ }
+ });
+ },
+
+ make_raw_material_request_dialog: function(r) {
+ var fields = [
+ {fieldtype:'Check', fieldname:'include_exploded_items',
+ label: __('Include Exploded Items')},
+ {fieldtype:'Check', fieldname:'ignore_existing_ordered_qty',
+ label: __('Ignore Existing Ordered Qty')},
+ {
+ fieldtype:'Table', fieldname: 'items',
+ description: __('Select BOM, Qty and For Warehouse'),
+ fields: [
+ {fieldtype:'Read Only', fieldname:'item_code',
+ label: __('Item Code'), in_list_view:1},
+ {fieldtype:'Link', fieldname:'bom', options: 'BOM', reqd: 1,
+ label: __('BOM'), in_list_view:1, get_query: function(doc) {
+ return {filters: {item: doc.item_code}};
+ }
+ },
+ {fieldtype:'Float', fieldname:'required_qty', reqd: 1,
+ label: __('Qty'), in_list_view:1},
+ {fieldtype:'Link', fieldname:'for_warehouse', options: 'Warehouse',
+ label: __('For Warehouse')}
+ ],
+ data: r.message,
+ get_data: function() {
+ return r.message
+ }
+ }
+ ]
+ var d = new frappe.ui.Dialog({
+ title: __("Select from Items having BOM"),
+ fields: fields,
+ primary_action: function() {
+ var data = d.get_values();
+ me.frm.call({
+ method: 'erpnext.selling.doctype.sales_order.sales_order.make_raw_material_request',
+ args: {
+ items: data,
+ company: me.frm.doc.company,
+ sales_order: me.frm.docname,
+ project: me.frm.project
+ },
+ freeze: true,
+ callback: function(r) {
+ if(r.message) {
+ frappe.msgprint(__('Material Request {0} submitted.',
+ ['<a href="#Form/Material Request/'+r.message.name+'">' + r.message.name+ '</a>']));
+ }
+ d.hide();
+ me.frm.reload_doc();
+ }
+ });
+ },
+ primary_action_label: __('Make')
+ });
+ d.show();
+ },
+
make_delivery_note_based_on_delivery_date: function() {
var me = this;
@@ -423,7 +505,7 @@
filters: {'parent': me.frm.doc.name}
}
}},
-
+
{"fieldtype": "Button", "label": __("Make Purchase Order"), "fieldname": "make_purchase_order", "cssClass": "btn-primary"},
]
});
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 22e5741..0f3b677 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
@@ -1178,6 +1179,70 @@
"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
+ },
+ {
+ "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
+ },
+ {
+ "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,
@@ -1205,6 +1270,38 @@
"unique": 0
},
{
+ "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
+ },
+ {
"allow_bulk_edit": 1,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
@@ -3918,7 +4015,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-08-21 14:44:44.011356",
+ "modified": "2018-11-12 20:00:35.272747",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index a7b4a3e..5f435ce 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -5,8 +5,9 @@
import frappe
import json
import frappe.utils
-from frappe.utils import cstr, flt, getdate, comma_and, cint, nowdate
+from frappe.utils import cstr, flt, getdate, comma_and, cint, nowdate, add_days
from frappe import _
+from six import string_types
from frappe.model.utils import get_fetch_values
from frappe.model.mapper import get_mapped_doc
from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty
@@ -17,6 +18,7 @@
from erpnext.selling.doctype.customer.customer import check_credit_limit
from erpnext.stock.doctype.item.item import get_item_defaults
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
+from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@@ -366,7 +368,7 @@
self.indicator_color = "green"
self.indicator_title = _("Paid")
- def get_work_order_items(self):
+ def get_work_order_items(self, for_raw_material_request=0):
'''Returns items with BOM that already do not have a linked work order'''
items = []
@@ -375,8 +377,13 @@
bom = get_default_bom_item(i.item_code)
if bom:
stock_qty = i.qty if i.doctype == 'Packed Item' else i.stock_qty
- pending_qty= stock_qty - flt(frappe.db.sql('''select sum(qty) from `tabWork Order`
+ if not for_raw_material_request:
+ total_work_order_qty = flt(frappe.db.sql('''select sum(qty) from `tabWork Order`
where production_item=%s and sales_order=%s and sales_order_item = %s and docstatus<2''', (i.item_code, self.name, i.name))[0][0])
+ pending_qty = stock_qty - total_work_order_qty
+ else:
+ pending_qty = stock_qty
+
if pending_qty:
items.append(dict(
name= i.name,
@@ -384,6 +391,7 @@
bom = bom,
warehouse = i.warehouse,
pending_qty = pending_qty,
+ required_qty = pending_qty if for_raw_material_request else 0,
sales_order_item = i.name
))
return items
@@ -846,7 +854,7 @@
or supplier_name like %(txt)s)
and name in (select supplier from `tabSales Order Item` where parent = %(parent)s)
and name not in (select supplier from `tabPurchase Order` po inner join `tabPurchase Order Item` poi
- on po.name=poi.parent where po.docstatus<2 and poi.sales_order=%(parent)s)
+ on po.name=poi.parent where po.docstatus<2 and poi.sales_order=%(parent)s)
order by
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
if(locate(%(_txt)s, supplier_name), locate(%(_txt)s, supplier_name), 99999),
@@ -902,3 +910,44 @@
bom = bom[0].name if bom else None
return bom
+
+@frappe.whitelist()
+def make_raw_material_request(items, company, sales_order, project=None):
+ if not frappe.has_permission("Sales Order", "write"):
+ frappe.throw(_("Not permitted"), frappe.PermissionError)
+
+ if isinstance(items, string_types):
+ items = frappe._dict(json.loads(items))
+
+ for item in items.get('items'):
+ item["include_exploded_items"] = items.get('include_exploded_items')
+ item["ignore_existing_ordered_qty"] = items.get('ignore_existing_ordered_qty')
+
+ raw_materials = get_items_for_material_requests(items, company)
+ if not raw_materials:
+ frappe.msgprint(_("Material Request not created, as quantity for Raw Materials already available."))
+
+ material_request = frappe.new_doc('Material Request')
+ material_request.update(dict(
+ doctype = 'Material Request',
+ transaction_date = nowdate(),
+ company = company,
+ requested_by = frappe.session.user,
+ material_request_type = 'Purchase'
+ ))
+ for item in raw_materials:
+ item_doc = frappe.get_cached_doc('Item', item.get('item_code'))
+ schedule_date = add_days(nowdate(), cint(item_doc.lead_time_days))
+ material_request.append('items', {
+ 'item_code': item.get('item_code'),
+ 'qty': item.get('quantity'),
+ 'schedule_date': schedule_date,
+ 'warehouse': item.get('warehouse'),
+ 'sales_order': sales_order,
+ 'project': project
+ })
+ material_request.insert()
+ material_request.flags.ignore_permissions = 1
+ material_request.run_method("set_missing_values")
+ material_request.submit()
+ return material_request
\ No newline at end of file
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 538ea55..65e91bc 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -11,8 +11,7 @@
from erpnext.selling.doctype.sales_order.sales_order import make_work_orders
from erpnext.controllers.accounts_controller import update_child_qty_rate
import json
-
-
+from erpnext.selling.doctype.sales_order.sales_order import make_raw_material_request
class TestSalesOrder(unittest.TestCase):
def tearDown(self):
frappe.set_user("Administrator")
@@ -327,9 +326,8 @@
self.assertRaises(frappe.CancelledLinkError, dn.submit)
def test_service_type_product_bundle(self):
- from erpnext.stock.doctype.item.test_item import make_item
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
-
+ from erpnext.stock.doctype.item.test_item import make_item
make_item("_Test Service Product Bundle", {"is_stock_item": 0})
make_item("_Test Service Product Bundle Item 1", {"is_stock_item": 0})
make_item("_Test Service Product Bundle Item 2", {"is_stock_item": 0})
@@ -343,9 +341,8 @@
self.assertTrue("_Test Service Product Bundle Item 2" in [d.item_code for d in so.packed_items])
def test_mix_type_product_bundle(self):
- from erpnext.stock.doctype.item.test_item import make_item
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
-
+ from erpnext.stock.doctype.item.test_item import make_item
make_item("_Test Mix Product Bundle", {"is_stock_item": 0})
make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1})
make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0})
@@ -388,11 +385,10 @@
def test_drop_shipping(self):
from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order_for_drop_shipment
- from erpnext.stock.doctype.item.test_item import make_item
from erpnext.buying.doctype.purchase_order.purchase_order import update_status
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
-
+ from erpnext.stock.doctype.item.test_item import make_item
po_item = make_item("_Test Item for Drop Shipping", {"is_stock_item": 1, "delivered_by_supplier": 1})
dn_item = make_item("_Test Regular Item", {"is_stock_item": 1})
@@ -585,8 +581,8 @@
self.assertEquals(wo_qty[0][0], so_item_name.get(item))
def test_serial_no_based_delivery(self):
- from erpnext.stock.doctype.item.test_item import make_item
frappe.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1)
+ from erpnext.stock.doctype.item.test_item import make_item
item = make_item("_Reserved_Serialized_Item", {"is_stock_item": 1,
"maintain_stock": 1,
"has_serial_no": 1,
@@ -685,12 +681,62 @@
se.cancel()
self.assertFalse(frappe.db.exists("Serial No", {"sales_order": so.name}))
+ def test_request_for_raw_materials(self):
+ from erpnext.stock.doctype.item.test_item import make_item
+ item = make_item("_Test Finished Item", {"is_stock_item": 1,
+ "maintain_stock": 1,
+ "valuation_rate": 500,
+ "item_defaults": [
+ {
+ "default_warehouse": "_Test Warehouse - _TC",
+ "company": "_Test Company"
+ }]
+ })
+ make_item("_Test Raw Item A", {"maintain_stock": 1,
+ "valuation_rate": 100,
+ "item_defaults": [
+ {
+ "default_warehouse": "_Test Warehouse - _TC",
+ "company": "_Test Company"
+ }]
+ })
+ make_item("_Test Raw Item B", {"maintain_stock": 1,
+ "valuation_rate": 200,
+ "item_defaults": [
+ {
+ "default_warehouse": "_Test Warehouse - _TC",
+ "company": "_Test Company"
+ }]
+ })
+ from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+ make_bom(item=item.item_code, rate=1000,
+ raw_materials = ['_Test Raw Item A', '_Test Raw Item B'])
+
+ so = make_sales_order(**{
+ "item_list": [{
+ "item_code": item.item_code,
+ "qty": 1,
+ "rate":1000
+ }]
+ })
+ so.submit()
+ mr_dict = frappe._dict()
+ items = so.get_work_order_items(1)
+ mr_dict['items'] = items
+ mr_dict['include_exploded_items'] = 0
+ mr_dict['ignore_existing_ordered_qty'] = 1
+ make_raw_material_request(mr_dict, so.company, so.name)
+ mr = frappe.db.sql("""select name from `tabMaterial Request` ORDER BY creation DESC LIMIT 1""", as_dict=1)[0]
+ mr_doc = frappe.get_doc('Material Request',mr.get('name'))
+ self.assertEqual(mr_doc.items[0].sales_order, so.name)
+
def make_sales_order(**args):
so = frappe.new_doc("Sales Order")
args = frappe._dict(args)
if args.transaction_date:
so.transaction_date = args.transaction_date
+ so.set_warehouse = "" # no need to test set_warehouse permission since it only affects the client
so.company = args.company or "_Test Company"
so.customer = args.customer or "_Test Customer"
so.currency = args.currency or "INR"
@@ -714,7 +760,7 @@
})
so.delivery_date = add_days(so.transaction_date, 10)
-
+
if not args.do_not_save:
so.insert()
if not args.do_not_submit:
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py
index ed28204..daec5b5 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.py
@@ -11,12 +11,9 @@
@frappe.whitelist()
def get_items(start, page_length, price_list, item_group, search_value="", pos_profile=None):
- serial_no = ""
- batch_no = ""
- barcode = ""
+ data = dict()
warehouse = ""
display_items_in_stock = 0
- item_code = search_value
if pos_profile:
warehouse, display_items_in_stock = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'display_items_in_stock'])
@@ -25,20 +22,12 @@
item_group = get_root_of('Item Group')
if search_value:
- # search serial no
- serial_no_data = frappe.db.get_value('Serial No', search_value, ['name', 'item_code'])
- if serial_no_data:
- serial_no, item_code = serial_no_data
+ data = search_serial_or_batch_or_barcode_number(search_value)
- if not serial_no:
- batch_no_data = frappe.db.get_value('Batch', search_value, ['name', 'item'])
- if batch_no_data:
- batch_no, item_code = batch_no_data
-
- if not serial_no and not batch_no:
- barcode_data = frappe.db.get_value('Item Barcode', {'barcode': search_value}, ['parent', 'barcode'])
- if barcode_data:
- item_code, barcode = barcode_data
+ item_code = data.get("item_code") if data.get("item_code") else search_value
+ serial_no = data.get("serial_no") if data.get("serial_no") else ""
+ batch_no = data.get("batch_no") if data.get("batch_no") else ""
+ barcode = data.get("barcode") if data.get("barcode") else ""
item_code, condition = get_conditions(item_code, serial_no, batch_no, barcode)
@@ -119,6 +108,23 @@
return res
+@frappe.whitelist()
+def search_serial_or_batch_or_barcode_number(search_value):
+ # search barcode no
+ barcode_data = frappe.db.get_value('Item Barcode', {'barcode': search_value}, ['barcode', 'parent as item_code'], as_dict=True)
+ if barcode_data:
+ return barcode_data
+
+ # search serial no
+ serial_no_data = frappe.db.get_value('Serial No', search_value, ['name as serial_no', 'item_code'], as_dict=True)
+ if serial_no_data:
+ return serial_no_data
+
+ # search batch no
+ batch_no_data = frappe.db.get_value('Batch', search_value, ['name as batch_no', 'item as item_code'], as_dict=True)
+ if batch_no_data:
+ return batch_no_data
+
def get_conditions(item_code, serial_no, batch_no, barcode):
if serial_no or batch_no or barcode:
return frappe.db.escape(item_code), "i.name = %(item_code)s"
diff --git a/erpnext/selling/report/sales_analytics/__init__.py b/erpnext/selling/report/sales_analytics/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/selling/report/sales_analytics/__init__.py
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js
new file mode 100644
index 0000000..7c9e3ec
--- /dev/null
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.js
@@ -0,0 +1,129 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Sales Analytics"] = {
+ "filters": [
+ {
+ fieldname: "tree_type",
+ label: __("Tree Type"),
+ fieldtype: "Select",
+ options: ["Customer Group","Customer","Item Group","Item","Territory"],
+ default: "Customer",
+ reqd: 1
+ },
+ {
+ fieldname: "doc_type",
+ label: __("based_on"),
+ fieldtype: "Select",
+ options: ["Sales Order","Delivery Note","Sales Invoice"],
+ default: "Sales Invoice",
+ reqd: 1
+ },
+ {
+ fieldname: "value_quantity",
+ label: __("Value Or Qty"),
+ fieldtype: "Select",
+ options: [
+ { "value": "Value", "label": __("Value") },
+ { "value": "Quantity", "label": __("Quantity") },
+ ],
+ default: "Value",
+ reqd: 1
+ },
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ default: frappe.defaults.get_user_default("year_start_date"),
+ reqd: 1
+ },
+ {
+ fieldname:"to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ default: frappe.defaults.get_user_default("year_end_date"),
+ reqd: 1
+ },
+ {
+ fieldname: "company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company"),
+ reqd: 1
+ },
+ {
+ fieldname: "range",
+ label: __("Range"),
+ fieldtype: "Select",
+ options: [
+ { "value": "Weekly", "label": __("Weekly") },
+ { "value": "Monthly", "label": __("Monthly") },
+ { "value": "Quarterly", "label": __("Quarterly") },
+ { "value": "Yearly", "label": __("Yearly") }
+ ],
+ default: "Monthly",
+ reqd: 1
+ }
+ ],
+ "formatter": function(value, row, column, data) {
+ if(!value){
+ value = 0
+ }
+ return value;
+ },
+ get_datatable_options(options) {
+ return Object.assign(options, {
+ checkboxColumn: true,
+ events: {
+ onCheckRow: function(data) {
+ row_name = data[2].content;
+ row_values = data.slice(5).map(function (column) {
+ return column.content;
+ })
+
+ entry = {
+ 'name':row_name,
+ 'values':row_values
+ }
+
+ let raw_data = frappe.query_report.chart.data;
+ let new_datasets = raw_data.datasets;
+
+ var found = false;
+
+ for(var i=0; i < new_datasets.length;i++){
+ if(new_datasets[i].name == row_name){
+ found = true;
+ new_datasets.splice(i,1);
+ break;
+ }
+ }
+
+ if(!found){
+ new_datasets.push(entry);
+ }
+
+ let new_data = {
+ labels: raw_data.labels,
+ datasets: new_datasets
+ }
+
+ setTimeout(() => {
+ frappe.query_report.chart.update(new_data)
+ },200)
+
+
+ setTimeout(() => {
+ frappe.query_report.chart.draw(true);
+ }, 800)
+
+ frappe.query_report.raw_chart_data = new_data;
+ },
+ }
+ })
+ },
+}
+
+
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.json b/erpnext/selling/report/sales_analytics/sales_analytics.json
new file mode 100644
index 0000000..5c95f28
--- /dev/null
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.json
@@ -0,0 +1,32 @@
+{
+ "add_total_row": 0,
+ "creation": "2018-09-21 12:46:29.451048",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2018-09-21 12:46:29.451048",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Analytics",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Sales Order",
+ "report_name": "Sales Analytics",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Stock User"
+ },
+ {
+ "role": "Maintenance User"
+ },
+ {
+ "role": "Accounts User"
+ },
+ {
+ "role": "Sales Manager"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py
new file mode 100644
index 0000000..ef9e666
--- /dev/null
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.py
@@ -0,0 +1,286 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _, scrub
+from frappe.utils import getdate, flt
+from six import iteritems
+from erpnext.accounts.utils import get_fiscal_year
+
+def execute(filters=None):
+ return Analytics(filters).run()
+
+class Analytics(object):
+ def __init__(self, filters=None):
+ self.filters = frappe._dict(filters or {})
+ self.date_field = 'transaction_date' \
+ if self.filters.doc_type in ['Sales Order', 'Purchase Order'] else 'posting_date'
+ self.months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+ self.get_period_date_ranges()
+
+ def run(self):
+ self.get_columns()
+ self.get_data()
+ self.get_chart_data()
+ return self.columns, self.data , None, self.chart
+
+ def get_columns(self):
+ self.columns =[{
+ "label": _(self.filters.tree_type + " ID"),
+ "options": self.filters.tree_type,
+ "fieldname": "entity",
+ "fieldtype": "Link",
+ "width": 140
+ }]
+ if self.filters.tree_type in ["Customer", "Supplier", "Item"]:
+ self.columns.append({
+ "label": _(self.filters.tree_type + " Name"),
+ "fieldname": "entity_name",
+ "fieldtype": "Data",
+ "width": 140
+ })
+ for dummy, end_date in self.periodic_daterange:
+ period = self.get_period(end_date)
+ self.columns.append({
+ "label": _(period),
+ "fieldname": scrub(period),
+ "fieldtype": "Float",
+ "width": 120
+ })
+
+ self.columns.append({
+ "label": _("Total"),
+ "fieldname": "total",
+ "fieldtype": "Float",
+ "width": 120
+ })
+
+ def get_data(self):
+ if self.filters.tree_type in ["Customer", "Supplier"]:
+ self.get_sales_transactions_based_on_customers_or_suppliers()
+ self.get_rows()
+
+ elif self.filters.tree_type == 'Item':
+ self.get_sales_transactions_based_on_items()
+ self.get_rows()
+
+ elif self.filters.tree_type in ["Customer Group", "Supplier Group", "Territory"]:
+ self.get_sales_transactions_based_on_customer_or_territory_group()
+ self.get_rows_by_group()
+
+ elif self.filters.tree_type == 'Item Group':
+ self.get_sales_transactions_based_on_item_group()
+ self.get_rows_by_group()
+
+ def get_sales_transactions_based_on_customers_or_suppliers(self):
+ if self.filters["value_quantity"] == 'Value':
+ value_field = "base_net_total as value_field"
+ else:
+ value_field = "total_qty as value_field"
+
+ if self.filters.tree_type == 'Customer':
+ entity = "customer as entity"
+ entity_name = "customer_name as entity_name"
+ else:
+ entity = "supplier as entity"
+ entity_name = "supplier_name as entity_name"
+
+ self.entries = frappe.get_all(self.filters.doc_type,
+ fields=[entity, entity_name, value_field, self.date_field],
+ filters = {
+ "docstatus": 1,
+ "company": self.filters.company,
+ self.date_field: ('between', [self.filters.from_date, self.filters.to_date])
+ }
+ )
+
+ self.entity_names = {}
+ for d in self.entries:
+ self.entity_names.setdefault(d.entity, d.entity_name)
+
+ def get_sales_transactions_based_on_items(self):
+
+ if self.filters["value_quantity"] == 'Value':
+ value_field = 'base_amount'
+ else:
+ value_field = 'qty'
+
+ self.entries = frappe.db.sql("""
+ select i.item_code as entity, i.item_name as entity_name, i.{value_field} as value_field, s.{date_field}
+ from `tab{doctype} Item` i , `tab{doctype}` s
+ where s.name = i.parent and i.docstatus = 1 and s.company = %s
+ and s.{date_field} between %s and %s
+ """
+ .format(date_field=self.date_field, value_field = value_field, doctype=self.filters.doc_type),
+ (self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1)
+
+ self.entity_names = {}
+ for d in self.entries:
+ self.entity_names.setdefault(d.entity, d.entity_name)
+
+ def get_sales_transactions_based_on_customer_or_territory_group(self):
+ if self.filters["value_quantity"] == 'Value':
+ value_field = "base_net_total as value_field"
+ else:
+ value_field = "total_qty as value_field"
+
+ if self.filters.tree_type == 'Customer Group':
+ entity_field = 'customer_group as entity'
+ elif self.filters.tree_type == 'Supplier Group':
+ entity_field = "supplier as entity"
+ self.get_supplier_parent_child_map()
+ else:
+ entity_field = "territory as entity"
+
+ self.entries = frappe.get_all(self.filters.doc_type,
+ fields=[entity_field, value_field, self.date_field],
+ filters = {
+ "docstatus": 1,
+ "company": self.filters.company,
+ self.date_field: ('between', [self.filters.from_date, self.filters.to_date])
+ }
+ )
+ self.get_groups()
+
+ def get_sales_transactions_based_on_item_group(self):
+ if self.filters["value_quantity"] == 'Value':
+ value_field = "base_amount"
+ else:
+ value_field = "qty"
+
+ self.entries = frappe.db.sql("""
+ select i.item_group as entity, i.{value_field} as value_field, s.{date_field}
+ from `tab{doctype} Item` i , `tab{doctype}` s
+ where s.name = i.parent and i.docstatus = 1 and s.company = %s
+ and s.{date_field} between %s and %s
+ """.format(date_field=self.date_field, value_field = value_field, doctype=self.filters.doc_type),
+ (self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1)
+
+ self.get_groups()
+
+ def get_rows(self):
+ self.data=[]
+ self.get_periodic_data()
+
+ for entity, period_data in iteritems(self.entity_periodic_data):
+ row = {
+ "entity": entity,
+ "entity_name": self.entity_names.get(entity)
+ }
+ total = 0
+ for dummy, end_date in self.periodic_daterange:
+ period = self.get_period(end_date)
+ amount = flt(period_data.get(period, 0.0))
+ row[scrub(period)] = amount
+ total += amount
+
+ row["total"] = total
+ self.data.append(row)
+
+ def get_rows_by_group(self):
+ self.get_periodic_data()
+ out = []
+
+ for d in reversed(self.group_entries):
+ row = {
+ "entity": d.name,
+ "indent": self.depth_map.get(d.name)
+ }
+ total = 0
+ for dummy, end_date in self.periodic_daterange:
+ period = self.get_period(end_date)
+ amount = flt(self.entity_periodic_data.get(d.name, {}).get(period, 0.0))
+ row[scrub(period)] = amount
+ if d.parent:
+ self.entity_periodic_data.setdefault(d.parent, frappe._dict()).setdefault(period, 0.0)
+ self.entity_periodic_data[d.parent][period] += amount
+ total += amount
+ row["total"] = total
+ out = [row] + out
+ self.data = out
+
+ def get_periodic_data(self):
+ self.entity_periodic_data = frappe._dict()
+
+ for d in self.entries:
+ if self.filters.tree_type == "Supplier Group":
+ d.entity = self.parent_child_map.get(d.entity)
+ period = self.get_period(d.get(self.date_field))
+ self.entity_periodic_data.setdefault(d.entity, frappe._dict()).setdefault(period, 0.0)
+ self.entity_periodic_data[d.entity][period] += flt(d.value_field)
+
+ def get_period(self, posting_date):
+ if self.filters.range == 'Weekly':
+ period = "Week " + str(posting_date.isocalendar()[1])
+ elif self.filters.range == 'Monthly':
+ period = self.months[posting_date.month - 1]
+ elif self.filters.range == 'Quarterly':
+ period = "Quarter " + str(((posting_date.month-1)//3)+1)
+ else:
+ year = get_fiscal_year(posting_date, company=self.filters.company)
+ period = str(year[2])
+
+ return period
+
+ def get_period_date_ranges(self):
+ from dateutil.relativedelta import relativedelta
+ from_date, to_date = getdate(self.filters.from_date), getdate(self.filters.to_date)
+
+ increment = {
+ "Monthly": 1,
+ "Quarterly": 3,
+ "Half-Yearly": 6,
+ "Yearly": 12
+ }.get(self.filters.range, 1)
+
+ self.periodic_daterange = []
+ for dummy in range(1, 53, increment):
+ if self.filters.range == "Weekly":
+ period_end_date = from_date + relativedelta(days=6)
+ else:
+ period_end_date = from_date + relativedelta(months=increment, days=-1)
+
+ if period_end_date > to_date:
+ period_end_date = to_date
+ self.periodic_daterange.append([from_date, period_end_date])
+
+ from_date = period_end_date + relativedelta(days=1)
+ if period_end_date == to_date:
+ break
+
+ def get_groups(self):
+ if self.filters.tree_type == "Territory":
+ parent = 'parent_territory'
+ if self.filters.tree_type == "Customer Group":
+ parent = 'parent_customer_group'
+ if self.filters.tree_type == "Item Group":
+ parent = 'parent_item_group'
+ if self.filters.tree_type == "Supplier Group":
+ parent = 'parent_supplier_group'
+
+ self.depth_map = frappe._dict()
+
+ self.group_entries = frappe.db.sql("""select name, lft, rgt , {parent} as parent
+ from `tab{tree}` order by lft"""
+ .format(tree=self.filters.tree_type, parent=parent), as_dict=1)
+
+ for d in self.group_entries:
+ if d.parent:
+ self.depth_map.setdefault(d.name, self.depth_map.get(d.parent) + 1)
+ else:
+ self.depth_map.setdefault(d.name, 0)
+
+ def get_supplier_parent_child_map(self):
+ self.parent_child_map = frappe._dict(frappe.db.sql(""" select name, supplier_group from `tabSupplier`"""))
+
+ def get_chart_data(self):
+ labels = [d.get("label") for d in self.columns[3:]]
+ self.chart = {
+ "data": {
+ 'labels': labels,
+ 'datasets':[
+ ]
+ },
+ "type": "line"
+ }
\ No newline at end of file
diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py
new file mode 100644
index 0000000..c9a00fa
--- /dev/null
+++ b/erpnext/selling/report/sales_analytics/test_analytics.py
@@ -0,0 +1,250 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+import frappe.defaults
+import unittest
+from erpnext.selling.report.sales_analytics.sales_analytics import execute
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+
+class TestAnalytics(unittest.TestCase):
+
+ def tearDown(self):
+ frappe.db.sql(""" DELETE FROM `tabSales Order` """)
+
+ def test_by_entity(self):
+ create_sales_order()
+
+ filters = {
+ 'doc_type': 'Sales Order',
+ 'range': 'Monthly',
+ 'to_date': '2018-03-31',
+ 'tree_type': 'Customer',
+ 'company': '_Test Company',
+ 'from_date': '2017-04-01',
+ 'value_quantity': 'Value'
+ }
+
+ report = execute(filters)
+
+ expected_data = [
+ {
+ "entity": "_Test Customer 1",
+ "entity_name": "_Test Customer 1",
+ "apr": 0.0,
+ "may": 0.0,
+ "jun": 0.0,
+ "jul": 0.0,
+ "aug": 0.0,
+ "sep": 0.0,
+ "oct": 0.0,
+ "nov": 0.0,
+ "dec": 0.0,
+ "jan": 0.0,
+ "feb": 2000.0,
+ "mar": 0.0,
+ "total":2000.0
+ },
+ {
+ "entity": "_Test Customer 3",
+ "entity_name": "_Test Customer 3",
+ "apr": 0.0,
+ "may": 0.0,
+ "jun": 2000.0,
+ "jul": 1000.0,
+ "aug": 0.0,
+ "sep": 0.0,
+ "oct": 0.0,
+ "nov": 0.0,
+ "dec": 0.0,
+ "jan": 0.0,
+ "feb": 0.0,
+ "mar": 0.0,
+ "total": 3000.0
+ },
+ {
+ "entity": "_Test Customer 2",
+ "entity_name": "_Test Customer 2",
+ "apr": 0.0,
+ "may": 0.0,
+ "jun": 0.0,
+ "jul": 0.0,
+ "aug": 0.0,
+ "sep": 1500.0,
+ "oct": 1000.0,
+ "nov": 0.0,
+ "dec": 0.0,
+ "jan": 0.0,
+ "feb": 0.0,
+ "mar": 0.0,
+ "total":2500.0
+ }
+ ]
+ self.assertEqual(expected_data, report[1])
+
+ def test_by_group(self):
+ create_sales_order()
+
+ filters = {
+ 'doc_type': 'Sales Order',
+ 'range': 'Monthly',
+ 'to_date': '2018-03-31',
+ 'tree_type': 'Customer Group',
+ 'company': '_Test Company',
+ 'from_date': '2017-04-01',
+ 'value_quantity': 'Value'
+ }
+
+ report = execute(filters)
+
+ expected_data = [
+ {
+ "entity": "All Customer Groups",
+ "indent": 0,
+ "apr": 0.0,
+ "may": 0.0,
+ "jun": 2000.0,
+ "jul": 1000.0,
+ "aug": 0.0,
+ "sep": 1500.0,
+ "oct": 1000.0,
+ "nov": 0.0,
+ "dec": 0.0,
+ "jan": 0.0,
+ "feb": 2000.0,
+ "mar": 0.0,
+ "total":7500.0
+ },
+ {
+ "entity": "Individual",
+ "indent": 1,
+ "apr": 0.0,
+ "may": 0.0,
+ "jun": 0.0,
+ "jul": 0.0,
+ "aug": 0.0,
+ "sep": 0.0,
+ "oct": 0.0,
+ "nov": 0.0,
+ "dec": 0.0,
+ "jan": 0.0,
+ "feb": 0.0,
+ "mar": 0.0,
+ "total": 0.0
+ },
+ {
+ "entity": "_Test Customer Group",
+ "indent": 1,
+ "apr": 0.0,
+ "may": 0.0,
+ "jun": 0.0,
+ "jul": 0.0,
+ "aug": 0.0,
+ "sep": 0.0,
+ "oct": 0.0,
+ "nov": 0.0,
+ "dec": 0.0,
+ "jan": 0.0,
+ "feb": 0.0,
+ "mar": 0.0,
+ "total":0.0
+ },
+ {
+ "entity": "_Test Customer Group 1",
+ "indent": 1,
+ "apr": 0.0,
+ "may": 0.0,
+ "jun": 0.0,
+ "jul": 0.0,
+ "aug": 0.0,
+ "sep": 0.0,
+ "oct": 0.0,
+ "nov": 0.0,
+ "dec": 0.0,
+ "jan": 0.0,
+ "feb": 0.0,
+ "mar": 0.0,
+ "total":0.0
+ }
+ ]
+ self.assertEqual(expected_data, report[1])
+
+ def test_by_quantity(self):
+ create_sales_order()
+
+ filters = {
+ 'doc_type': 'Sales Order',
+ 'range': 'Monthly',
+ 'to_date': '2018-03-31',
+ 'tree_type': 'Customer',
+ 'company': '_Test Company',
+ 'from_date': '2017-04-01',
+ 'value_quantity': 'Quantity'
+ }
+
+ report = execute(filters)
+
+ expected_data = [
+ {
+ "entity": "_Test Customer 1",
+ "entity_name": "_Test Customer 1",
+ "apr": 0.0,
+ "may": 0.0,
+ "jun": 0.0,
+ "jul": 0.0,
+ "aug": 0.0,
+ "sep": 0.0,
+ "oct": 0.0,
+ "nov": 0.0,
+ "dec": 0.0,
+ "jan": 0.0,
+ "feb": 20.0,
+ "mar": 0.0,
+ "total":20.0
+ },
+ {
+ "entity": "_Test Customer 3",
+ "entity_name": "_Test Customer 3",
+ "apr": 0.0,
+ "may": 0.0,
+ "jun": 20.0,
+ "jul": 10.0,
+ "aug": 0.0,
+ "sep": 0.0,
+ "oct": 0.0,
+ "nov": 0.0,
+ "dec": 0.0,
+ "jan": 0.0,
+ "feb": 0.0,
+ "mar": 0.0,
+ "total": 30.0
+ },
+ {
+ "entity": "_Test Customer 2",
+ "entity_name": "_Test Customer 2",
+ "apr": 0.0,
+ "may": 0.0,
+ "jun": 0.0,
+ "jul": 0.0,
+ "aug": 0.0,
+ "sep": 15.0,
+ "oct": 10.0,
+ "nov": 0.0,
+ "dec": 0.0,
+ "jan": 0.0,
+ "feb": 0.0,
+ "mar": 0.0,
+ "total":25.0
+ }
+ ]
+ self.assertEqual(expected_data, report[1])
+
+def create_sales_order():
+ frappe.set_user("Administrator")
+
+ make_sales_order(qty=10, customer = "_Test Customer 1", transaction_date='2018-02-10')
+ make_sales_order(qty=10, customer = "_Test Customer 1", transaction_date='2018-02-15')
+ make_sales_order(qty=15, customer = "_Test Customer 2", transaction_date='2017-09-23')
+ make_sales_order(qty=10, customer = "_Test Customer 2", transaction_date='2017-10-10')
+ make_sales_order(qty=20, customer = "_Test Customer 3", transaction_date='2017-06-15')
+ make_sales_order(qty=10, customer = "_Test Customer 3", transaction_date='2017-07-10')
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 9377cad..01f8956 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
@@ -730,6 +731,38 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fieldname": "standard_working_hours",
+ "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": "Standard Working Hours",
+ "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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "default_terms",
"fieldtype": "Link",
"hidden": 0,
@@ -837,7 +870,7 @@
"label": "Create Chart Of Accounts Based On",
"length": 0,
"no_copy": 0,
- "options": "\nStandard Template\nExisting Company",
+ "options": "\nStandard Template\nExisting Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -871,7 +904,7 @@
"label": "Chart Of Accounts Template",
"length": 0,
"no_copy": 1,
- "options": "",
+ "options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -1158,39 +1191,39 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "round_off_cost_center",
- "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": "Round Off Cost Center",
- "length": 0,
- "no_copy": 0,
- "options": "Cost Center",
- "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,
+ "fieldname": "round_off_cost_center",
+ "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": "Round Off Cost Center",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Cost Center",
+ "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
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "write_off_account",
"fieldtype": "Link",
"hidden": 0,
@@ -1491,7 +1524,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.__islocal",
- "fieldname": "default_deferred_expense_account",
+ "fieldname": "default_deferred_expense_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
@@ -1500,7 +1533,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Default Deferred Expense Account",
+ "label": "Default Deferred Expense Account",
"length": 0,
"no_copy": 1,
"options": "Account",
@@ -1525,7 +1558,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.__islocal",
- "fieldname": "default_payroll_payable_account",
+ "fieldname": "default_payroll_payable_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
@@ -1534,7 +1567,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Default Payroll Payable Account",
+ "label": "Default Payroll Payable Account",
"length": 0,
"no_copy": 1,
"options": "Account",
@@ -1558,20 +1591,20 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "eval:!doc.__islocal",
- "fieldname": "default_expense_claim_payable_account",
+ "depends_on": "eval:!doc.__islocal",
+ "fieldname": "default_expense_claim_payable_account",
"fieldtype": "Link",
"hidden": 0,
- "ignore_user_permissions": 1,
+ "ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Default Expense Claim Payable Account",
+ "label": "Default Expense Claim Payable Account",
"length": 0,
- "no_copy": 1,
- "options": "Account",
+ "no_copy": 1,
+ "options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -2870,8 +2903,8 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2018-09-13 10:00:47.915706",
- "modified_by": "Administrator",
+ "modified": "2018-10-24 12:57:46.776452",
+ "modified_by": "Administrator",
"module": "Setup",
"name": "Company",
"owner": "Administrator",
diff --git a/erpnext/setup/doctype/company/company_dashboard.py b/erpnext/setup/doctype/company/company_dashboard.py
index 868284a..6133226 100644
--- a/erpnext/setup/doctype/company/company_dashboard.py
+++ b/erpnext/setup/doctype/company/company_dashboard.py
@@ -13,7 +13,7 @@
'goal_doctype_link': 'company',
'goal_field': 'base_grand_total',
'date_field': 'posting_date',
- 'filter_str': 'status != "Draft"',
+ 'filter_str': 'docstatus = 1',
'aggregation': 'sum'
},
diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py
index 309866b..0216c3b 100644
--- a/erpnext/setup/utils.py
+++ b/erpnext/setup/utils.py
@@ -98,7 +98,7 @@
if not value:
import requests
- api_url = "https://frankfurter.erpnext.org/{0}".format(transaction_date)
+ api_url = "https://frankfurter.app/{0}".format(transaction_date)
response = requests.get(api_url, params={
"base": from_currency,
"symbols": to_currency
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 9620b31..9a15484 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -21,6 +21,9 @@
return (doc.docstatus==1 || doc.qty<=doc.actual_qty) ? "green" : "orange"
})
+ erpnext.queries.setup_queries(frm, "Warehouse", function() {
+ return erpnext.queries.warehouse(frm.doc);
+ });
erpnext.queries.setup_warehouse_query(frm);
frm.set_query('project', function(doc) {
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index 765033a..8c96be5 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
@@ -1333,6 +1334,169 @@
"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": "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
+ },
+ {
+ "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
+ },
+ {
+ "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
+ },
+ {
+ "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
+ },
+ {
+ "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
},
{
"allow_bulk_edit": 0,
@@ -1341,8 +1505,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "items_section",
- "fieldtype": "Section Break",
+ "fieldname": "scan_barcode",
+ "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -1350,12 +1514,11 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "",
+ "label": "Scan Barcode",
"length": 0,
"no_copy": 0,
- "oldfieldtype": "Section Break",
- "options": "fa fa-shopping-cart",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3751,73 +3914,38 @@
"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,
- "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
- },
- {
- "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,
+ "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
},
{
@@ -4168,7 +4296,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2018-10-06 14:25:15.326772",
+ "modified": "2018-11-12 20:01:34.432403",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",
@@ -4282,4 +4410,4 @@
"track_changes": 1,
"track_seen": 1,
"track_views": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js
index 9ec2a38..9631264 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js
@@ -10,5 +10,47 @@
} else if (doc.grand_total === 0 || flt(doc.per_billed, 2) == 100) {
return [__("Completed"), "green", "per_billed,=,100"];
}
+ },
+ onload: function (doclist) {
+ const action = () => {
+ const selected_docs = doclist.get_checked_items();
+ const docnames = doclist.get_checked_items(true);
+
+ if (selected_docs.length > 0) {
+ for (let doc of selected_docs) {
+ if (!doc.docstatus) {
+ frappe.throw(__("Cannot create a Delivery Trip from Draft documents."));
+ }
+ };
+
+ frappe.new_doc("Delivery Trip")
+ .then(() => {
+ // Empty out the child table before inserting new ones
+ cur_frm.set_value("delivery_stops", []);
+
+ // We don't want to use `map_current_doc` since it brings up
+ // the dialog to select more items. We just want the mapper
+ // function to be called.
+ frappe.call({
+ type: "POST",
+ method: "frappe.model.mapper.map_docs",
+ args: {
+ "method": "erpnext.stock.doctype.delivery_note.delivery_note.make_delivery_trip",
+ "source_names": docnames,
+ "target_doc": cur_frm.doc
+ },
+ callback: function (r) {
+ if (!r.exc) {
+ frappe.model.sync(r.message);
+ cur_frm.dirty();
+ cur_frm.refresh();
+ }
+ }
+ });
+ })
+ };
+ };
+
+ doclist.page.add_actions_menu_item(__('Create Delivery Trip'), action, false);
}
};
diff --git a/erpnext/stock/doctype/delivery_stop/delivery_stop.json b/erpnext/stock/doctype/delivery_stop/delivery_stop.json
index 7bce72d..5610a81 100644
--- a/erpnext/stock/doctype/delivery_stop/delivery_stop.json
+++ b/erpnext/stock/doctype/delivery_stop/delivery_stop.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@@ -176,6 +177,39 @@
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.docstatus==1",
+ "fieldname": "visited",
+ "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": "Visited",
+ "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_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -762,7 +796,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2018-10-11 22:32:27.450906",
+ "modified": "2018-10-16 05:23:25.661542",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Stop",
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js
index a38c6d7..a9e2f88 100755
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js
@@ -3,6 +3,8 @@
frappe.ui.form.on('Delivery Trip', {
setup: function (frm) {
+ frm.set_indicator_formatter('customer', (stop) => (stop.visited) ? "green" : "orange");
+
frm.set_query("driver", function () {
return {
filters: {
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.json b/erpnext/stock/doctype/delivery_trip/delivery_trip.json
index a9236e8..1d32ecd 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.json
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@@ -540,7 +541,7 @@
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
- "allow_on_submit": 0,
+ "allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
@@ -575,6 +576,70 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "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,
+ "options": "Draft\nScheduled\nIn Transit\nCompleted\nCancelled",
+ "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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "cb_more_info",
+ "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
+ },
+ {
+ "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,
@@ -611,7 +676,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-10-11 22:32:04.355068",
+ "modified": "2018-10-22 08:25:42.323147",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Trip",
@@ -663,6 +728,7 @@
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
+ "title_field": "driver_name",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
index 431eb6c..01b4734 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
@@ -27,9 +27,14 @@
self.validate_stop_addresses()
def on_submit(self):
+ self.update_status()
self.update_delivery_notes()
+ def on_update_after_submit(self):
+ self.update_status()
+
def on_cancel(self):
+ self.update_status()
self.update_delivery_notes(delete=True)
def validate_stop_addresses(self):
@@ -37,6 +42,22 @@
if not stop.customer_address:
stop.customer_address = get_address_display(frappe.get_doc("Address", stop.address).as_dict())
+ def update_status(self):
+ status = {
+ 0: "Draft",
+ 1: "Scheduled",
+ 2: "Cancelled"
+ }[self.docstatus]
+
+ if self.docstatus == 1:
+ visited_stops = [stop.visited for stop in self.delivery_stops]
+ if all(visited_stops):
+ status = "Completed"
+ elif any(visited_stops):
+ status = "In Transit"
+
+ self.db_set("status", status)
+
def update_delivery_notes(self, delete=False):
"""
Update all connected Delivery Notes with Delivery Trip details
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js b/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js
new file mode 100644
index 0000000..1d198b7
--- /dev/null
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js
@@ -0,0 +1,12 @@
+frappe.listview_settings['Delivery Trip'] = {
+ add_fields: ["status"],
+ get_indicator: function (doc) {
+ if (in_list(["Cancelled", "Draft"], doc.status)) {
+ return [__(doc.status), "red", "status,=," + doc.status];
+ } else if (in_list(["In Transit", "Scheduled"], doc.status)) {
+ return [__(doc.status), "orange", "status,=," + doc.status];
+ } else if (doc.status === "Completed") {
+ return [__(doc.status), "green", "status,=," + doc.status];
+ }
+ }
+};
diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
index b0a3d31..76b6fcd 100644
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
@@ -9,7 +9,7 @@
import frappe
from erpnext.stock.doctype.delivery_trip.delivery_trip import get_contact_and_address, notify_customers
from erpnext.tests.utils import create_test_contact_and_address
-from frappe.utils import add_days, now_datetime
+from frappe.utils import add_days, flt, now_datetime, nowdate
class TestDeliveryTrip(unittest.TestCase):
@@ -72,6 +72,33 @@
self.assertEqual(len(route_list[0]), 2) # [home_address, locked_stop]
self.assertEqual(len(route_list[1]), 3) # [locked_stop, second_stop, home_address]
+ def test_delivery_trip_status_draft(self):
+ self.assertEqual(self.delivery_trip.status, "Draft")
+
+ def test_delivery_trip_status_scheduled(self):
+ self.delivery_trip.submit()
+ self.assertEqual(self.delivery_trip.status, "Scheduled")
+
+ def test_delivery_trip_status_cancelled(self):
+ self.delivery_trip.submit()
+ self.delivery_trip.cancel()
+ self.assertEqual(self.delivery_trip.status, "Cancelled")
+
+ def test_delivery_trip_status_in_transit(self):
+ self.delivery_trip.submit()
+ self.delivery_trip.delivery_stops[0].visited = 1
+ self.delivery_trip.save()
+ self.assertEqual(self.delivery_trip.status, "In Transit")
+
+ def test_delivery_trip_status_completed(self):
+ self.delivery_trip.submit()
+
+ for stop in self.delivery_trip.delivery_stops:
+ stop.visited = 1
+
+ self.delivery_trip.save()
+ self.assertEqual(self.delivery_trip.status, "Completed")
+
def create_driver():
if not frappe.db.exists("Driver", "Newton Scmander"):
@@ -108,11 +135,11 @@
"make": "Maruti",
"model": "PCM",
"last_odometer": 5000,
- "acquisition_date": frappe.utils.nowdate(),
+ "acquisition_date": nowdate(),
"location": "Mumbai",
"chassis_no": "1234ABCD",
"uom": "Litre",
- "vehicle_value": frappe.utils.flt(500000)
+ "vehicle_value": flt(500000)
})
vehicle.insert()
diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json
index c0285cb..8a53a27 100644
--- a/erpnext/stock/doctype/material_request/material_request.json
+++ b/erpnext/stock/doctype/material_request/material_request.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
@@ -313,6 +314,38 @@
"unique": 0
},
{
+ "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
+ },
+ {
"allow_bulk_edit": 1,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
@@ -826,7 +859,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2018-09-05 07:28:01.070112",
+ "modified": "2018-10-18 04:41:56.818108",
"modified_by": "Administrator",
"module": "Stock",
"name": "Material Request",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 736ba8b..1d9eb99 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -21,26 +21,9 @@
})
},
onload: function(frm) {
- $.each(["warehouse", "rejected_warehouse"], function(i, field) {
- frm.set_query(field, "items", function() {
- return {
- filters: [
- ["Warehouse", "company", "in", ["", cstr(frm.doc.company)]],
- ["Warehouse", "is_group", "=", 0]
- ]
- }
- })
- })
-
- frm.set_query("supplier_warehouse", function() {
- return {
- filters: [
- ["Warehouse", "company", "in", ["", cstr(frm.doc.company)]],
- ["Warehouse", "is_group", "=", 0]
- ]
- }
+ erpnext.queries.setup_queries(frm, "Warehouse", function() {
+ return erpnext.queries.warehouse(frm.doc);
});
-
},
onload_post_render: function(frm) {
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index f2da856..3e9ff49 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
@@ -1078,6 +1079,211 @@
"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
+ },
+ {
+ "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 Accepted 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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "Warehouse where you are maintaining stock of rejected items",
+ "fieldname": "rejected_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": "Rejected Warehouse",
+ "length": 0,
+ "no_copy": 1,
+ "oldfieldname": "rejected_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
+ },
+ {
+ "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,
+ "label": "",
+ "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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "No",
+ "description": "",
+ "fieldname": "is_subcontracted",
+ "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": "Raw Materials Supplied",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "is_subcontracted",
+ "oldfieldtype": "Select",
+ "options": "No\nYes",
+ "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_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.is_subcontracted==\"Yes\"",
+ "description": "",
+ "fieldname": "supplier_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": "Supplier Warehouse",
+ "length": 0,
+ "no_copy": 1,
+ "oldfieldname": "supplier_warehouse",
+ "oldfieldtype": "Link",
+ "options": "Warehouse",
+ "permlevel": 0,
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "print_width": "50px",
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": "50px"
+ },
+ {
+ "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,
@@ -1176,6 +1382,75 @@
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
+ "collapsible": 1,
+ "collapsible_depends_on": "supplied_items",
+ "columns": 0,
+ "description": "",
+ "fieldname": "raw_material_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": "Raw Materials Supplied",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldtype": "Section Break",
+ "options": "fa fa-table",
+ "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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "supplied_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": "Supplied Items",
+ "length": 0,
+ "no_copy": 1,
+ "oldfieldname": "pr_raw_material_details",
+ "oldfieldtype": "Table",
+ "options": "Purchase Receipt Item Supplied",
+ "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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break0",
@@ -2628,148 +2903,6 @@
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
- "collapsible": 1,
- "collapsible_depends_on": "supplied_items",
- "columns": 0,
- "description": "",
- "fieldname": "raw_material_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": "Raw Materials Supplied",
- "length": 0,
- "no_copy": 0,
- "oldfieldtype": "Section Break",
- "options": "fa fa-table",
- "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
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "No",
- "description": "",
- "fieldname": "is_subcontracted",
- "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": "Raw Materials Supplied",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "is_subcontracted",
- "oldfieldtype": "Select",
- "options": "No\nYes",
- "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_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
- "fieldname": "supplier_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": "Supplier Warehouse",
- "length": 0,
- "no_copy": 1,
- "oldfieldname": "supplier_warehouse",
- "oldfieldtype": "Link",
- "options": "Warehouse",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "print_width": "50px",
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0,
- "width": "50px"
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "supplied_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": "Supplied Items",
- "length": 0,
- "no_copy": 1,
- "oldfieldname": "pr_raw_material_details",
- "oldfieldtype": "Table",
- "options": "Purchase Receipt Item Supplied",
- "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
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bill_no",
@@ -2908,41 +3041,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "description": "Warehouse where you are maintaining stock of rejected items",
- "fieldname": "rejected_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": "Rejected Warehouse",
- "length": 0,
- "no_copy": 1,
- "oldfieldname": "rejected_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
- },
- {
- "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": 1,
@@ -3610,7 +3708,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2018-08-21 14:44:34.419727",
+ "modified": "2018-11-02 19:59:01.423485",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index e482f58..f006d00 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -185,7 +185,8 @@
if warehouse_account.get(d.warehouse):
stock_value_diff = frappe.db.get_value("Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": self.name,
- "voucher_detail_no": d.name}, "stock_value_difference")
+ "voucher_detail_no": d.name, "warehouse": d.warehouse}, "stock_value_difference")
+
if not stock_value_diff:
continue
gl_entries.append(self.get_gl_dict({
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 0356b0e..a26992a 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -594,6 +594,11 @@
erpnext.utils.add_item(this.frm);
},
+ scan_barcode: function() {
+ let transaction_controller= new erpnext.TransactionController({frm:this.frm});
+ transaction_controller.scan_barcode();
+ },
+
on_submit: function() {
this.clean_up();
},
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index 35f8c27..6a925ad 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
@@ -1051,6 +1052,38 @@
"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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "items",
"fieldtype": "Table",
"hidden": 0,
@@ -1965,7 +1998,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
- "set_only_once": 0,
+ "set_only_once": 0,
"translatable": 0,
"unique": 0
},
@@ -2048,7 +2081,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-09-05 06:27:59.630826",
+ "modified": "2018-10-18 04:42:41.452572",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index d4e7b95..d8e4656 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -634,19 +634,19 @@
ret = frappe._dict({
'uom' : item.stock_uom,
- 'stock_uom' : item.stock_uom,
+ 'stock_uom' : item.stock_uom,
'description' : item.description,
- 'image' : item.image,
+ 'image' : item.image,
'item_name' : item.item_name,
'expense_account' : args.get("expense_account"),
'cost_center' : get_default_cost_center(args, item, item_group_defaults),
- 'qty' : 0,
- 'transfer_qty' : 0,
+ 'qty' : args.get("qty"),
+ 'transfer_qty' : args.get('qty'),
'conversion_factor' : 1,
- 'batch_no' : '',
+ 'batch_no' : '',
'actual_qty' : 0,
'basic_rate' : 0,
- 'serial_no' : '',
+ 'serial_no' : '',
'has_serial_no' : item.has_serial_no,
'has_batch_no' : item.has_batch_no,
'sample_quantity' : item.sample_quantity
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py
index 97d24c6..65de2e5 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.py
@@ -26,8 +26,9 @@
frappe.msgprint (_("`Freeze Stocks Older Than` should be smaller than %d days.") %stock_frozen_limit)
# show/hide barcode field
- frappe.make_property_setter({'fieldname': 'barcodes', 'property': 'hidden',
- 'value': 0 if self.show_barcode_field else 1})
+ for name in ["barcode", "barcodes", "scan_barcode"]:
+ frappe.make_property_setter({'fieldname': name, 'property': 'hidden',
+ 'value': 0 if self.show_barcode_field else 1})
self.cant_change_valuation_method()
self.validate_clean_description_html()
diff --git a/erpnext/stock/report/stock_analytics/__init__.py b/erpnext/stock/report/stock_analytics/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/report/stock_analytics/__init__.py
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.js b/erpnext/stock/report/stock_analytics/stock_analytics.js
new file mode 100644
index 0000000..6010ea9
--- /dev/null
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.js
@@ -0,0 +1,136 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Stock Analytics"] = {
+ "filters": [
+ {
+ fieldname: "item_group",
+ label: __("Item Group"),
+ fieldtype: "Link",
+ options:"Item Group",
+ default: "",
+ },
+ {
+ fieldname: "item_code",
+ label: __("Item"),
+ fieldtype: "Link",
+ options:"Item",
+ default: "",
+ },
+ {
+ fieldname: "value_quantity",
+ label: __("Value Or Qty"),
+ fieldtype: "Select",
+ options: [
+ { "value": "Value", "label": __("Value") },
+ { "value": "Quantity", "label": __("Quantity") }
+ ],
+ default: "Value",
+ reqd: 1
+ },
+ {
+ fieldname: "brand",
+ label: __("Brand"),
+ fieldtype: "Link",
+ options:"Brand",
+ default: "",
+ },
+ {
+ fieldname: "warehouse",
+ label: __("Warehouse"),
+ fieldtype: "Link",
+ options:"Warehouse",
+ default: "",
+ },
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ default: frappe.defaults.get_global_default("year_start_date"),
+ reqd: 1
+ },
+ {
+ fieldname:"to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ default: frappe.defaults.get_global_default("year_end_date"),
+ reqd: 1
+ },
+ {
+ fieldname: "range",
+ label: __("Range"),
+ fieldtype: "Select",
+ options: [
+ { "value": "Weekly", "label": __("Weekly") },
+ { "value": "Monthly", "label": __("Monthly") },
+ { "value": "Quarterly", "label": __("Quarterly") },
+ { "value": "Yearly", "label": __("Yearly") }
+ ],
+ default: "Monthly",
+ reqd: 1
+ }
+ ],
+ "formatter": function(value, row, column, data) {
+ if(!value && (column.fieldname == 'brand' || column.fieldname == 'uom')){
+ value = ""
+ }
+
+ if(Number(value)){
+ value = value.toFixed(2)
+ }
+
+ return value;
+ },
+ get_datatable_options(options) {
+ return Object.assign(options, {
+ checkboxColumn: true,
+ events: {
+ onCheckRow: function(data) {
+ row_name = data[2].content;
+ row_values = data.slice(6).map(function (column) {
+ return column.content;
+ })
+
+ entry = {
+ 'name':row_name,
+ 'values':row_values
+ }
+
+ let raw_data = frappe.query_report.chart.data;
+ let new_datasets = raw_data.datasets;
+
+ var found = false;
+
+ for(var i=0; i < new_datasets.length;i++){
+ if(new_datasets[i].name == row_name){
+ found = true;
+ new_datasets.splice(i,1);
+ break;
+ }
+ }
+
+ if(!found){
+ new_datasets.push(entry);
+ }
+
+ let new_data = {
+ labels: raw_data.labels,
+ datasets: new_datasets
+ }
+
+ setTimeout(() => {
+ frappe.query_report.chart.update(new_data)
+ },200)
+
+
+ setTimeout(() => {
+ frappe.query_report.chart.draw(true);
+ }, 800)
+
+ frappe.query_report.raw_chart_data = new_data;
+ },
+ }
+ })
+ },
+}
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.json b/erpnext/stock/report/stock_analytics/stock_analytics.json
new file mode 100644
index 0000000..efd5e99
--- /dev/null
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.json
@@ -0,0 +1,32 @@
+{
+ "add_total_row": 0,
+ "creation": "2018-10-08 12:11:32.133020",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2018-10-08 12:18:42.834270",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Stock Analytics",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Stock Entry",
+ "report_name": "Stock Analytics",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Manufacturing Manager"
+ },
+ {
+ "role": "Stock Manager"
+ },
+ {
+ "role": "Stock User"
+ },
+ {
+ "role": "Manufacturing User"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py
new file mode 100644
index 0000000..5a8a672
--- /dev/null
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.py
@@ -0,0 +1,185 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _, scrub
+from frappe.utils import getdate, flt
+from erpnext.stock.report.stock_balance.stock_balance import (get_items, get_stock_ledger_entries, get_item_details)
+from erpnext.accounts.utils import get_fiscal_year
+from six import iteritems
+
+def execute(filters=None):
+ filters = frappe._dict(filters or {})
+ columns = get_columns(filters)
+ data = get_data(filters)
+ chart = get_chart_data(columns)
+
+ return columns, data, None, chart
+
+def get_columns(filters):
+ columns = [
+ {
+ "label": _("Item"),
+ "options":"Item",
+ "fieldname": "name",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Item Name"),
+ "options":"Item",
+ "fieldname": "item_name",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Item Group"),
+ "options":"Item Group",
+ "fieldname": "item_group",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Brand"),
+ "fieldname": "brand",
+ "fieldtype": "Data",
+ "width": 120
+ },
+ {
+ "label": _("UOM"),
+ "fieldname": "uom",
+ "fieldtype": "Data",
+ "width": 120
+ }]
+
+ ranges = get_period_date_ranges(filters)
+
+ for dummy, end_date in ranges:
+ period = get_period(end_date, filters)
+
+ columns.append({
+ "label": _(period),
+ "fieldname":scrub(period),
+ "fieldtype": "Float",
+ "width": 120
+ })
+
+ return columns
+
+def get_period_date_ranges(filters):
+ from dateutil.relativedelta import relativedelta
+ from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
+
+ increment = {
+ "Monthly": 1,
+ "Quarterly": 3,
+ "Half-Yearly": 6,
+ "Yearly": 12
+ }.get(filters.range,1)
+
+ periodic_daterange = []
+ for dummy in range(1, 53, increment):
+ if filters.range == "Weekly":
+ period_end_date = from_date + relativedelta(days=6)
+ else:
+ period_end_date = from_date + relativedelta(months=increment, days=-1)
+
+ if period_end_date > to_date:
+ period_end_date = to_date
+ periodic_daterange.append([from_date, period_end_date])
+
+ from_date = period_end_date + relativedelta(days=1)
+ if period_end_date == to_date:
+ break
+
+ return periodic_daterange
+
+def get_period(posting_date, filters):
+ months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+
+ if filters.range == 'Weekly':
+ period = "Week " + str(posting_date.isocalendar()[1])
+ elif filters.range == 'Monthly':
+ period = months[posting_date.month - 1]
+ elif filters.range == 'Quarterly':
+ period = "Quarter " + str(((posting_date.month-1)//3)+1)
+ else:
+ year = get_fiscal_year(posting_date, company=filters.company)
+ period = str(year[2])
+
+ return period
+
+
+def get_periodic_data(entry, filters):
+ periodic_data = {}
+ for d in entry:
+ period = get_period(d.posting_date, filters)
+ bal_qty = 0
+
+ if d.voucher_type == "Stock Reconciliation":
+ if periodic_data.get(d.item_code):
+ bal_qty = periodic_data[d.item_code]["balance"]
+
+ qty_diff = d.qty_after_transaction - bal_qty
+ else:
+ qty_diff = d.actual_qty
+
+ if filters["value_quantity"] == 'Quantity':
+ value = qty_diff
+ else:
+ value = d.stock_value_difference
+
+ periodic_data.setdefault(d.item_code, {}).setdefault(period, 0.0)
+ periodic_data.setdefault(d.item_code, {}).setdefault("balance", 0.0)
+
+ periodic_data[d.item_code]["balance"] += value
+ periodic_data[d.item_code][period] = periodic_data[d.item_code]["balance"]
+
+
+ return periodic_data
+
+def get_data(filters):
+ data = []
+ items = get_items(filters)
+ sle = get_stock_ledger_entries(filters, items)
+ item_details = get_item_details(items, sle, filters)
+ periodic_data = get_periodic_data(sle, filters)
+ ranges = get_period_date_ranges(filters)
+
+ for dummy, item_data in iteritems(item_details):
+ row = {
+ "name": item_data.name,
+ "item_name": item_data.item_name,
+ "item_group": item_data.item_group,
+ "uom": item_data.stock_uom,
+ "brand": item_data.brand,
+ }
+ total = 0
+ for dummy, end_date in ranges:
+ period = get_period(end_date, filters)
+ amount = flt(periodic_data.get(item_data.name, {}).get(period))
+ row[scrub(period)] = amount
+ total += amount
+ row["total"] = total
+ data.append(row)
+
+ return data
+
+def get_chart_data(columns):
+ labels = [d.get("label") for d in columns[4:]]
+ chart = {
+ "data": {
+ 'labels': labels,
+ 'datasets':[
+ { "values": ['0' for d in columns[4:]] }
+ ]
+ }
+ }
+ chart["type"] = "line"
+
+ return chart
+
+
+
+
diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py
index 6f984a5..a2867c8 100644
--- a/erpnext/utilities/product.py
+++ b/erpnext/utilities/product.py
@@ -108,8 +108,8 @@
uom_conversion_factor = frappe.db.sql("""select C.conversion_factor
from `tabUOM Conversion Detail` C
- inner join `tabItem` I on C.uom = I.sales_uom
- where C.parent = %s""", item_code)
+ inner join `tabItem` I on C.parent = I.name and C.uom = I.sales_uom
+ where I.name = %s""", item_code)
uom_conversion_factor = uom_conversion_factor[0][0] if uom_conversion_factor else 1
price_obj["formatted_price_sales_uom"] = fmt_money(price_obj["price_list_rate"] * uom_conversion_factor, currency=price_obj["currency"])