From c819ea38a0712f8b99a7c274794c51ed7b5f1966 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Tue, 25 Sep 2018 14:53:12 +0500 Subject: [PATCH 001/129] -Added Item Default child table for Brand -Item Default Precedence: Get defaults from Item Group, if no default, fetch from Brand -Remove auto set Item's Item Defaults from Item Group: Item Default should be manually entered to override both Item Group and Brand defaults --- erpnext/setup/doctype/brand/brand.json | 431 ++++++++++++++----------- erpnext/setup/doctype/brand/brand.py | 16 +- erpnext/stock/get_item_details.py | 32 +- 3 files changed, 282 insertions(+), 197 deletions(-) diff --git a/erpnext/setup/doctype/brand/brand.json b/erpnext/setup/doctype/brand/brand.json index 2914ef1bc8..5558358078 100644 --- a/erpnext/setup/doctype/brand/brand.json +++ b/erpnext/setup/doctype/brand/brand.json @@ -1,204 +1,269 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:brand", - "beta": 0, - "creation": "2013-02-22 01:27:54", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:brand", + "beta": 0, + "creation": "2013-02-22 01:27:54", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "brand", - "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": "Brand Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "brand", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "brand", + "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": "Brand Name", + "length": 0, + "no_copy": 0, + "oldfieldname": "brand", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Description", + "length": 0, + "no_copy": 0, + "oldfieldname": "description", + "oldfieldtype": "Text", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0, "width": "300px" + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "defaults", + "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": "Defaults", + "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": "brand_defaults", + "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": "Brand Defaults", + "length": 0, + "no_copy": 0, + "options": "Item Default", + "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 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-certificate", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-29 06:35:44.740318", - "modified_by": "Administrator", - "module": "Setup", - "name": "Brand", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "fa fa-certificate", + "idx": 1, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-09-25 13:27:37.090157", + "modified_by": "Administrator", + "module": "Setup", + "name": "Brand", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Item Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Item Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 1, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 1, + "sort_order": "ASC", + "track_changes": 0, + "track_seen": 0, "track_views": 0 } \ No newline at end of file diff --git a/erpnext/setup/doctype/brand/brand.py b/erpnext/setup/doctype/brand/brand.py index d90aa5a9b6..12839d18ae 100644 --- a/erpnext/setup/doctype/brand/brand.py +++ b/erpnext/setup/doctype/brand/brand.py @@ -3,8 +3,22 @@ from __future__ import unicode_literals import frappe +import copy from frappe.model.document import Document class Brand(Document): - pass \ No newline at end of file + pass + +def get_brand_defaults(item, company): + item = frappe.get_cached_doc("Item", item) + if item.brand: + brand = frappe.get_cached_doc("Brand", item.brand) + + for d in brand.brand_defaults or []: + if d.company == company: + row = copy.deepcopy(d.as_dict()) + row.pop("name") + return row + + return frappe._dict() \ No newline at end of file diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 01ee9db1ca..ae643f7818 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -13,6 +13,7 @@ from erpnext.stock.doctype.batch.batch import get_batch_no from erpnext import get_company_currency from erpnext.stock.doctype.item.item import get_item_defaults, get_uom_conv_factor from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults +from erpnext.setup.doctype.brand.brand import get_brand_defaults from six import string_types, iteritems @@ -218,9 +219,10 @@ def get_basic_details(args, item): item_defaults = get_item_defaults(item.name, args.company) item_group_defaults = get_item_group_defaults(item.name, args.company) + brand_defaults = get_brand_defaults(item.name, args.company) warehouse = user_default_warehouse or item_defaults.get("default_warehouse") or\ - item_group_defaults.get("default_warehouse") or args.warehouse + item_group_defaults.get("default_warehouse") or brand_defaults.get("default_warehouse") or args.warehouse if args.get('doctype') == "Material Request" and not args.get('material_request_type'): args['material_request_type'] = frappe.db.get_value('Material Request', @@ -242,9 +244,9 @@ def get_basic_details(args, item): "description": cstr(item.description).strip(), "image": cstr(item.image).strip(), "warehouse": warehouse, - "income_account": get_default_income_account(args, item_defaults, item_group_defaults), - "expense_account": get_default_expense_account(args, item_defaults, item_group_defaults), - "cost_center": get_default_cost_center(args, item_defaults, item_group_defaults), + "income_account": get_default_income_account(args, item_defaults, item_group_defaults, brand_defaults), + "expense_account": get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults), + "cost_center": get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults), 'has_serial_no': item.has_serial_no, 'has_batch_no': item.has_batch_no, "batch_no": None, @@ -263,7 +265,7 @@ def get_basic_details(args, item): "net_rate": 0.0, "net_amount": 0.0, "discount_percentage": 0.0, - "supplier": get_default_supplier(args, item_defaults, item_group_defaults), + "supplier": get_default_supplier(args, item_defaults, item_group_defaults, brand_defaults), "update_stock": args.get("update_stock") if args.get('doctype') in ['Sales Invoice', 'Purchase Invoice'] else 0, "delivered_by_supplier": item.delivered_by_supplier if args.get("doctype") in ["Sales Order", "Sales Invoice"] else 0, "is_fixed_asset": item.is_fixed_asset, @@ -325,14 +327,16 @@ def calculate_service_end_date(args, item=None): return deferred_detail -def get_default_income_account(args, item, item_group): +def get_default_income_account(args, item, item_group, brand): return (item.get("income_account") or item_group.get("income_account") + or brand.get("income_account") or args.income_account) -def get_default_expense_account(args, item, item_group): +def get_default_expense_account(args, item, item_group, brand): return (item.get("expense_account") or item_group.get("expense_account") + or brand.get("expense_account") or args.expense_account) def get_default_deferred_account(args, item, fieldname=None): @@ -343,22 +347,23 @@ def get_default_deferred_account(args, item, fieldname=None): else: return None -def get_default_cost_center(args, item, item_group): +def get_default_cost_center(args, item, item_group, brand): cost_center = None if args.get('project'): cost_center = frappe.db.get_value("Project", args.get("project"), "cost_center", cache=True) if not cost_center: if args.get('customer'): - cost_center = item.get('selling_cost_center') or item_group.get('selling_cost_center') + cost_center = item.get('selling_cost_center') or item_group.get('selling_cost_center') or brand.get('selling_cost_center') else: - cost_center = item.get('buying_cost_center') or item_group.get('buying_cost_center') + cost_center = item.get('buying_cost_center') or item_group.get('buying_cost_center') or brand.get('buying_cost_center') return cost_center or args.get("cost_center") -def get_default_supplier(args, item, item_group): +def get_default_supplier(args, item, item_group, brand): return (item.get("default_supplier") - or item_group.get("default_supplier")) + or item_group.get("default_supplier") + or brand.get("default_supplier")) def get_price_list_rate(args, item_doc, out): meta = frappe.get_meta(args.parenttype or args.doctype) @@ -825,10 +830,11 @@ def get_default_bom(item_code=None): def get_valuation_rate(item_code, company, warehouse=None): item = get_item_defaults(item_code, company) item_group = get_item_group_defaults(item_code, company) + brand = get_brand_defaults(item_code, company) # item = frappe.get_doc("Item", item_code) if item.get("is_stock_item"): if not warehouse: - warehouse = item.get("default_warehouse") or item_group.get("default_warehouse") + warehouse = item.get("default_warehouse") or item_group.get("default_warehouse") or brand.get("default_warehouse") return frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, ["valuation_rate"], as_dict=True) or {"valuation_rate": 0} From 127b3f552b62f40db1d88cc2a29758ddf972b51c Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Wed, 26 Sep 2018 23:23:56 +0500 Subject: [PATCH 002/129] At least add a row for old logic to work properly --- erpnext/stock/doctype/item/item.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 566b6386fe..e2922b33a0 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -128,6 +128,7 @@ class Item(WebsiteGenerator): self.validate_uom_conversion_factor() self.validate_item_defaults() self.update_defaults_from_item_group() + self.update_defaults_add_company() if not self.get("__islocal"): self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group") @@ -135,6 +136,10 @@ class Item(WebsiteGenerator): from `tabWebsite Item Group` where parentfield='website_item_groups' and parenttype='Item' and parent=%s""", self.name) + def update_defaults_add_company(self): + if not self.item_defaults: + self.append("item_defaults", {"company": frappe.defaults.get_defaults().company}) + def on_update(self): invalidate_cache_for_item(self) self.validate_name_with_item_group() From 7d9ee83d5ae3af28feae380098b267751a293a07 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Wed, 26 Sep 2018 23:41:12 +0500 Subject: [PATCH 003/129] -Fix for updated get_default_cost_center -Back to not updating item's defaults child table --- erpnext/stock/doctype/item/item.py | 11 ++--------- erpnext/stock/doctype/stock_entry/stock_entry.py | 4 +++- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index e2922b33a0..a1aa6b14bd 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -86,7 +86,7 @@ class Item(WebsiteGenerator): def after_insert(self): '''set opening stock and item price''' if self.standard_rate: - for default in self.item_defaults: + for default in self.item_defaults or [frappe._dict()]: self.add_price(default.default_price_list) if self.opening_stock: @@ -126,9 +126,6 @@ class Item(WebsiteGenerator): self.validate_fixed_asset() self.validate_retain_sample() self.validate_uom_conversion_factor() - self.validate_item_defaults() - self.update_defaults_from_item_group() - self.update_defaults_add_company() if not self.get("__islocal"): self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group") @@ -136,10 +133,6 @@ class Item(WebsiteGenerator): from `tabWebsite Item Group` where parentfield='website_item_groups' and parenttype='Item' and parent=%s""", self.name) - def update_defaults_add_company(self): - if not self.item_defaults: - self.append("item_defaults", {"company": frappe.defaults.get_defaults().company}) - def on_update(self): invalidate_cache_for_item(self) self.validate_name_with_item_group() @@ -181,7 +174,7 @@ class Item(WebsiteGenerator): from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry # default warehouse, or Stores - for default in self.item_defaults: + for default in self.item_defaults or [frappe._dict({'company': frappe.defaults.get_defaults().company})]: default_warehouse = (default.default_warehouse or frappe.db.get_single_value('Stock Settings', 'default_warehouse') or frappe.db.get_value('Warehouse', {'warehouse_name': _('Stores')})) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 5d3c6c4adc..5c6555a7a1 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -10,6 +10,7 @@ from erpnext.stock.utils import get_incoming_rate from erpnext.stock.stock_ledger import get_previous_sle, NegativeStockError, get_valuation_rate from erpnext.stock.get_item_details import get_bin_details, get_default_cost_center, get_conversion_factor, get_reserved_qty_for_so from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults +from erpnext.setup.doctype.brand.brand import get_brand_defaults from erpnext.stock.doctype.batch.batch import get_batch_no, set_batch_nos, get_batch_qty from erpnext.stock.doctype.item.item import get_item_defaults from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, add_additional_cost @@ -631,6 +632,7 @@ class StockEntry(StockController): item = item[0] item_group_defaults = get_item_group_defaults(item.name, self.company) + brand_defaults = get_brand_defaults(item.name, self.company) ret = frappe._dict({ 'uom' : item.stock_uom, @@ -639,7 +641,7 @@ class StockEntry(StockController): '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), + 'cost_center' : get_default_cost_center(args, item, item_group_defaults, brand_defaults), 'qty' : args.get("qty"), 'transfer_qty' : args.get('qty'), 'conversion_factor' : 1, From 07acc2bf5a68456a7ccd17402d62bd7fb761038b Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Tue, 23 Oct 2018 23:18:16 +0500 Subject: [PATCH 004/129] Allow brand in quick entry --- erpnext/setup/doctype/brand/brand.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/brand/brand.json b/erpnext/setup/doctype/brand/brand.json index 5558358078..03e025c762 100644 --- a/erpnext/setup/doctype/brand/brand.json +++ b/erpnext/setup/doctype/brand/brand.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -14,7 +15,7 @@ "fields": [ { "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, + "allow_in_quick_entry": 1, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -156,7 +157,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-09-25 13:27:37.090157", + "modified": "2018-10-23 23:18:06.067612", "modified_by": "Administrator", "module": "Setup", "name": "Brand", From 1ea98cbbe1f1d93adac518c5c4c354615fd2f8b0 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Fri, 28 Dec 2018 00:29:43 +0500 Subject: [PATCH 005/129] fix: missing line due to conflict resolution --- erpnext/stock/doctype/item/item.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index a1aa6b14bd..a9360011ff 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -126,6 +126,7 @@ class Item(WebsiteGenerator): self.validate_fixed_asset() self.validate_retain_sample() self.validate_uom_conversion_factor() + self.validate_item_defaults() if not self.get("__islocal"): self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group") From 00aaa4877945a9258638405e308c1943bdffe692 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Sat, 2 Feb 2019 02:43:44 +0500 Subject: [PATCH 006/129] test(Item): test_item_defaults --- .../accounts/doctype/account/test_account.py | 2 + erpnext/setup/doctype/brand/test_records.json | 11 ++++ erpnext/stock/doctype/item/test_item.py | 55 +++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index acaa0966a2..1c3375050e 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -129,6 +129,8 @@ def _make_test_records(verbose): ["_Test Write Off", "Indirect Expenses", 0, None, None], ["_Test Exchange Gain/Loss", "Indirect Expenses", 0, None, None], + ["_Test Account Sales", "Direct Income", 0, None, None], + # related to Account Inventory Integration ["_Test Account Stock In Hand", "Current Assets", 0, None, None], diff --git a/erpnext/setup/doctype/brand/test_records.json b/erpnext/setup/doctype/brand/test_records.json index d2a4ad4e23..17b5a6b0a3 100644 --- a/erpnext/setup/doctype/brand/test_records.json +++ b/erpnext/setup/doctype/brand/test_records.json @@ -2,5 +2,16 @@ { "brand": "_Test Brand", "doctype": "Brand" + }, + { + "brand": "_Test Brand With Item Defaults", + "doctype": "Brand", + "brand_defaults": [{ + "company": "_Test Company", + "expense_account": "_Test Account Cost for Goods Sold - _TC", + "income_account": "_Test Account Sales - _TC", + "buying_cost_center": "_Test Cost Center - _TC", + "selling_cost_center": "_Test Cost Center - _TC" + }] } ] \ No newline at end of file diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index d02559ebcb..6ec7ba1d1f 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -102,6 +102,61 @@ class TestItem(unittest.TestCase): for key, value in iteritems(to_check): self.assertEqual(value, details.get(key)) + def test_item_defaults(self): + frappe.delete_doc_if_exists("Item", "Test Item With Defaults", force=1) + make_item("Test Item With Defaults", { + "item_group": "_Test Item Group", + "brand": "_Test Brand With Item Defaults", + "item_defaults": [{ + "company": "_Test Company", + "default_warehouse": "_Test Warehouse 2 - _TC", # no override + "expense_account": "_Test Account Stock Expenses - _TC", # override brand default + "buying_cost_center": "_Test Write Off Cost Center - _TC", # override item group default + }] + }) + + sales_item_check = { + "item_code": "Test Item With Defaults", + "warehouse": "_Test Warehouse 2 - _TC", # from item + "income_account": "_Test Account Sales - _TC", # from brand + "expense_account": "_Test Account Stock Expenses - _TC", # from item + "cost_center": "_Test Cost Center 2 - _TC", # from item group + } + sales_item_details = get_item_details({ + "item_code": "Test Item With Defaults", + "company": "_Test Company", + "price_list": "_Test Price List", + "currency": "_Test Currency", + "doctype": "Sales Invoice", + "conversion_rate": 1, + "price_list_currency": "_Test Currency", + "plc_conversion_rate": 1, + "customer": "_Test Customer", + }) + for key, value in iteritems(sales_item_check): + self.assertEqual(value, sales_item_details.get(key)) + + purchase_item_check = { + "item_code": "Test Item With Defaults", + "warehouse": "_Test Warehouse 2 - _TC", # from item + "expense_account": "_Test Account Stock Expenses - _TC", # from item + "income_account": "_Test Account Sales - _TC", # from brand + "cost_center": "_Test Write Off Cost Center - _TC" # from item + } + purchase_item_details = get_item_details({ + "item_code": "Test Item With Defaults", + "company": "_Test Company", + "price_list": "_Test Price List", + "currency": "_Test Currency", + "doctype": "Purchase Invoice", + "conversion_rate": 1, + "price_list_currency": "_Test Currency", + "plc_conversion_rate": 1, + "supplier": "_Test Supplier", + }) + for key, value in iteritems(purchase_item_check): + self.assertEqual(value, purchase_item_details.get(key)) + def test_item_attribute_change_after_variant(self): frappe.delete_doc_if_exists("Item", "_Test Variant Item-L", force=1) From a032f0528e4e8567fe07d0ed7ab786f8d629a6e8 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 11 Feb 2019 13:12:44 +0530 Subject: [PATCH 007/129] new report gross-and-net-profit-report --- erpnext/accounts/doctype/account/account.json | 35 +++- .../accounts/report/financial_statements.py | 9 +- .../gross_and_net_profit_report/__init__.py | 0 .../gross_and_net_profit_report.html | 1 + .../gross_and_net_profit_report.js | 51 ++++++ .../gross_and_net_profit_report.json | 30 ++++ .../gross_and_net_profit_report.py | 163 ++++++++++++++++++ 7 files changed, 285 insertions(+), 4 deletions(-) create mode 100644 erpnext/accounts/report/gross_and_net_profit_report/__init__.py create mode 100644 erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.html create mode 100644 erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js create mode 100644 erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.json create mode 100644 erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index e47f8d2245..876a3922c9 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -632,6 +632,39 @@ "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.account_type==\"Income Account\") || (doc.account_type==\"Expense Account\")) && (doc.is_group != 1))", + "fieldname": "include_in_gross", + "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": "Include in gross", + "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 } ], "has_web_view": 0, @@ -645,7 +678,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-01-07 16:52:02.557837", + "modified": "2019-02-08 11:30:46.790603", "modified_by": "Administrator", "module": "Accounts", "name": "Account", diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 09cf5b1d2f..fd84bd0c56 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -126,7 +126,7 @@ def get_label(periodicity, from_date, to_date): def get_data( company, root_type, balance_must_be, period_list, filters=None, accumulated_values=1, only_current_fiscal_year=True, ignore_closing_entries=False, - ignore_accumulated_values_for_fy=False): + ignore_accumulated_values_for_fy=False , total = True): accounts = get_accounts(company, root_type) if not accounts: @@ -154,7 +154,7 @@ def get_data( out = prepare_data(accounts, balance_must_be, period_list, company_currency) out = filter_out_zero_value_rows(out, parent_children_map) - if out: + if out and total: add_total_row(out, root_type, balance_must_be, period_list, company_currency) return out @@ -218,6 +218,9 @@ def prepare_data(accounts, balance_must_be, period_list, company_currency): "year_start_date": year_start_date, "year_end_date": year_end_date, "currency": company_currency, + "include_in_gross": d.include_in_gross, + "account_type": d.account_type, + "is_group": d.is_group, "opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be=="Debit" else -1), "account_name": ('%s - %s' %(_(d.account_number), _(d.account_name)) if d.account_number else _(d.account_name)) @@ -285,7 +288,7 @@ def add_total_row(out, root_type, balance_must_be, period_list, company_currency def get_accounts(company, root_type): return frappe.db.sql(""" - select name, account_number, parent_account, lft, rgt, root_type, report_type, account_name + select name, account_number, parent_account, lft, rgt, root_type, report_type, account_name, include_in_gross, account_type, is_group, lft, rgt from `tabAccount` where company=%s and root_type=%s order by lft""", (company, root_type), as_dict=True) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/__init__.py b/erpnext/accounts/report/gross_and_net_profit_report/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.html b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.html new file mode 100644 index 0000000000..40ba20c4ac --- /dev/null +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.html @@ -0,0 +1 @@ +{% include "accounts/report/financial_statements.html" %} \ No newline at end of file diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js new file mode 100644 index 0000000000..63ac281cdb --- /dev/null +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js @@ -0,0 +1,51 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Gross and Net Profit Report"] = { + "filters": [ + + ] +} +frappe.require("assets/erpnext/js/financial_statements.js", function() { + frappe.query_reports["Gross and Net Profit Report"] = $.extend({}, + erpnext.financial_statements); + + frappe.query_reports["Gross and Net Profit Report"]["filters"].push( + { + "fieldname":"project", + "label": __("Project"), + "fieldtype": "MultiSelect", + get_data: function() { + var projects = frappe.query_report.get_filter_value("project") || ""; + + const values = projects.split(/\s*,\s*/).filter(d => d); + const txt = projects.match(/[^,\s*]*$/)[0] || ''; + let data = []; + + frappe.call({ + type: "GET", + method:'frappe.desk.search.search_link', + async: false, + no_spinner: true, + args: { + doctype: "Project", + txt: txt, + filters: { + "name": ["not in", values] + } + }, + callback: function(r) { + data = r.results; + } + }); + return data; + } + }, + { + "fieldname": "accumulated_values", + "label": __("Accumulated Values"), + "fieldtype": "Check" + } + ); +}); diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.json b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.json new file mode 100644 index 0000000000..994b47faef --- /dev/null +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "creation": "2019-02-08 10:58:55.763090", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2019-02-08 10:58:55.763090", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Gross and Net Profit Report", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "GL Entry", + "report_name": "Gross and Net Profit Report", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ] +} \ No newline at end of file diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py new file mode 100644 index 0000000000..6645dc3691 --- /dev/null +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -0,0 +1,163 @@ +# 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 _ +from frappe.utils import flt +from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data) +import copy +from pprint import pprint + + +def execute(filters=None): + period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, + filters.periodicity, filters.accumulated_values, filters.company) + + columns, data = [], [] + + income = get_data(filters.company, "Income", "Credit", period_list, filters = filters, + accumulated_values=filters.accumulated_values, + ignore_closing_entries=True, ignore_accumulated_values_for_fy= True, total= False) + + expense = get_data(filters.company, "Expense", "Debit", period_list, filters=filters, + accumulated_values=filters.accumulated_values, + ignore_closing_entries=True, ignore_accumulated_values_for_fy= True, total= False) + + columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company) + + data.append({"account_name": "'" + _("Included in Gross Profit") + "'", + "account": "'" + _("Included in Gross Profit") + "'"}) + + gross_income = get_revenue(income, period_list, 'gross') + data.append({}) + data.extend(gross_income or []) + + gross_expense = get_revenue(expense, period_list, 'gross') + data.append({}) + data.extend(gross_expense or []) + + data.append({}) + gross_profit = get_profit(gross_income, gross_expense, period_list, filters.company, 'Gross Profit',filters.presentation_currency) + data.append(gross_profit) + + non_gross_income = get_revenue(income, period_list, 'non_gross') + data.append({}) + data.extend(non_gross_income or []) + + non_gross_expense = get_revenue(expense, period_list, 'non_gross') + data.append({}) + data.extend(non_gross_expense or []) + + net_profit =get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expense, period_list, filters.company,filters.presentation_currency) + data.append({}) + data.append(net_profit) + + return columns, data + +def get_revenue(data, period_list, revenue_type): + + if revenue_type == 'gross': + gross = [item for item in data if item['include_in_gross']==1 or item['is_group']==1] + gross, status = remove_parent_with_no_child(gross, period_list) + while status == "data to be removed": + gross, status = remove_parent_with_no_child(gross, period_list) + gross = adjust_account(gross, period_list) + return copy.deepcopy(gross) + elif revenue_type == 'non_gross': + non_gross = [item for item in data if item['include_in_gross']==0 or item['is_group']==1] + non_gross, status = remove_parent_with_no_child(non_gross, period_list) + while status == "data to be removed": + non_gross, status = remove_parent_with_no_child(non_gross, period_list) + non_gross = adjust_account(non_gross, period_list) + return copy.deepcopy(non_gross) + +def remove_parent_with_no_child(data, period_list): + status = "nothing to remove" + for parent in data: + if 'is_group' in parent and parent["is_group"] == 1: + have_child = False + for child in data: + if 'parent_account' in child and child["parent_account"] == parent["account"]: + have_child = True + break + + if not have_child: + status = "data to be removed" + data.remove(parent) + + return data, status + +def adjust_account(data, period_list, consolidated= False): + leaf_nodes = [item for item in data if item['is_group'] == 0] + totals = {} + for node in leaf_nodes: + set_total(node, node["total"], data, totals) + for d in data: + for period in period_list: + key = period if consolidated else period.key + d[key] = totals[d["account"]] + d['total'] = totals[d["account"]] + return data + +def set_total(node, value, complete_list, totals): + if not totals.get(node['account']): + totals[node["account"]] = 0 + totals[node["account"]] += value + + parent = node['parent_account'] + if not parent == '': + return set_total(next(item for item in complete_list if item['account'] == parent), value, complete_list, totals) + + +def get_profit(gross_income, gross_expense, period_list, company, profit_type, currency=None, consolidated=False): + + total = 0 + + profit_loss = { + "account_name": "'" + _(profit_type) + "'", + "account": "'" + _(profit_type) + "'", + "warn_if_negative": True, + "currency": currency or frappe.get_cached_value('Company', company, "default_currency") + } + + has_value = False + + for period in period_list: + key = period if consolidated else period.key + profit_loss[key] = flt(gross_income[0][key]) - flt(gross_expense[0][key]) + + if profit_loss[key]: + has_value=True + + total += flt(profit_loss[key]) + profit_loss['total'] = total + + if has_value: + return profit_loss + +def get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expense, period_list, company, currency=None, consolidated=False): + total = 0 + profit_loss = { + "account_name": "'" + _("Net Profit") + "'", + "account": "'" + _("Net Profit") + "'", + "warn_if_negative": True, + "currency": currency or frappe.get_cached_value('Company', company, "default_currency") + } + + has_value = False + + for period in period_list: + key = period if consolidated else period.key + total_income = flt(gross_income[0][key]) + flt(non_gross_income[0][key]) + total_expense = flt(gross_expense[0][key]) + flt(non_gross_expense[0][key]) + profit_loss[key] = flt(total_income) - flt(total_expense) + + if profit_loss[key]: + has_value=True + + total += flt(profit_loss[key]) + profit_loss['total'] = total + + if has_value: + return profit_loss From d635f722fc7f7c38c4988fdd77eddf2b77e3ae1c Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Mon, 11 Feb 2019 15:16:25 +0530 Subject: [PATCH 008/129] fix(stock_balance): Make balance qty and value columns more visible --- erpnext/stock/report/stock_balance/stock_balance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 14b1852a7f..d6b171f93c 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -79,14 +79,14 @@ def get_columns(): {"label": _("Description"), "fieldname": "description", "width": 140}, {"label": _("Warehouse"), "fieldname": "warehouse", "fieldtype": "Link", "options": "Warehouse", "width": 100}, {"label": _("Stock UOM"), "fieldname": "stock_uom", "fieldtype": "Link", "options": "UOM", "width": 90}, + {"label": _("Balance Qty"), "fieldname": "bal_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"}, + {"label": _("Balance Value"), "fieldname": "bal_val", "fieldtype": "Currency", "width": 100}, {"label": _("Opening Qty"), "fieldname": "opening_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"}, {"label": _("Opening Value"), "fieldname": "opening_val", "fieldtype": "Float", "width": 110}, {"label": _("In Qty"), "fieldname": "in_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"}, {"label": _("In Value"), "fieldname": "in_val", "fieldtype": "Float", "width": 80}, {"label": _("Out Qty"), "fieldname": "out_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"}, {"label": _("Out Value"), "fieldname": "out_val", "fieldtype": "Float", "width": 80}, - {"label": _("Balance Qty"), "fieldname": "bal_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"}, - {"label": _("Balance Value"), "fieldname": "bal_val", "fieldtype": "Currency", "width": 100}, {"label": _("Valuation Rate"), "fieldname": "val_rate", "fieldtype": "Currency", "width": 90, "convertible": "rate"}, {"label": _("Reorder Level"), "fieldname": "reorder_level", "fieldtype": "Float", "width": 80, "convertible": "qty"}, {"label": _("Reorder Qty"), "fieldname": "reorder_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"}, From f70d4089bc3a1b99b79c5ec7d90b8533fe8e514a Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 11 Feb 2019 17:02:20 +0530 Subject: [PATCH 009/129] fix: codacy --- .../gross_and_net_profit_report/gross_and_net_profit_report.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py index 6645dc3691..a5d6ec9974 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -7,7 +7,6 @@ from frappe import _ from frappe.utils import flt from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data) import copy -from pprint import pprint def execute(filters=None): From 4f0fd38209e3c1db85b98f2859ab7275b50e3a91 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 13 Feb 2019 16:46:05 +0530 Subject: [PATCH 010/129] refractor --- .../gross_and_net_profit_report.py | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py index a5d6ec9974..9096c257ab 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -25,14 +25,23 @@ def execute(filters=None): columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company) + + gross_income = get_revenue(income, period_list, 'gross') + + gross_expense = get_revenue(expense, period_list, 'gross') + + if(len(gross_income)==0 and len(gross_expense)== 0): + data.append({"account_name": "'" + _("Nothing is included in gross") + "'", + "account": "'" + _("Nothing is included in gross") + "'"}) + + return columns, data + data.append({"account_name": "'" + _("Included in Gross Profit") + "'", "account": "'" + _("Included in Gross Profit") + "'"}) - gross_income = get_revenue(income, period_list, 'gross') data.append({}) data.extend(gross_income or []) - gross_expense = get_revenue(expense, period_list, 'gross') data.append({}) data.extend(gross_expense or []) @@ -48,7 +57,7 @@ def execute(filters=None): data.append({}) data.extend(non_gross_expense or []) - net_profit =get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expense, period_list, filters.company,filters.presentation_currency) + net_profit = get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expense, period_list, filters.company,filters.presentation_currency) data.append({}) data.append(net_profit) @@ -124,7 +133,7 @@ def get_profit(gross_income, gross_expense, period_list, company, profit_type, c for period in period_list: key = period if consolidated else period.key - profit_loss[key] = flt(gross_income[0][key]) - flt(gross_expense[0][key]) + profit_loss[key] = flt(gross_income[0][key] if len(gross_income) else 0) - flt(gross_expense[0][key] if len(gross_expense) else 0) if profit_loss[key]: has_value=True @@ -148,8 +157,8 @@ def get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expe for period in period_list: key = period if consolidated else period.key - total_income = flt(gross_income[0][key]) + flt(non_gross_income[0][key]) - total_expense = flt(gross_expense[0][key]) + flt(non_gross_expense[0][key]) + total_income = flt(gross_income[0][key] if len(gross_income) else 0) + flt(non_gross_income[0][key] if len(non_gross_income) else 0) + total_expense = flt(gross_expense[0][key] if len(gross_expense) else 0) + flt(non_gross_expense[0][key] if len(non_gross_expense) else 0) profit_loss[key] = flt(total_income) - flt(total_expense) if profit_loss[key]: From 61f981ae05eeab7b53267a1a156869cfb5722d94 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 18 Feb 2019 11:43:32 +0530 Subject: [PATCH 011/129] Changes Requested --- .../gross_and_net_profit_report.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py index 9096c257ab..fe767c0409 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -66,19 +66,14 @@ def execute(filters=None): def get_revenue(data, period_list, revenue_type): if revenue_type == 'gross': - gross = [item for item in data if item['include_in_gross']==1 or item['is_group']==1] - gross, status = remove_parent_with_no_child(gross, period_list) - while status == "data to be removed": - gross, status = remove_parent_with_no_child(gross, period_list) - gross = adjust_account(gross, period_list) - return copy.deepcopy(gross) + revenue = [item for item in data if item['include_in_gross']==1 or item['is_group']==1] elif revenue_type == 'non_gross': - non_gross = [item for item in data if item['include_in_gross']==0 or item['is_group']==1] - non_gross, status = remove_parent_with_no_child(non_gross, period_list) - while status == "data to be removed": - non_gross, status = remove_parent_with_no_child(non_gross, period_list) - non_gross = adjust_account(non_gross, period_list) - return copy.deepcopy(non_gross) + revenue = [item for item in data if item['include_in_gross']==0 or item['is_group']==1] + revenue, status = remove_parent_with_no_child(revenue, period_list) + while status == "data to be removed": + revenue, status = remove_parent_with_no_child(revenue, period_list) + revenue = adjust_account(revenue, period_list) + return copy.deepcopy(revenue) def remove_parent_with_no_child(data, period_list): status = "nothing to remove" From 42a106c7ba94f009f381b86d90281e200b033291 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Fri, 15 Feb 2019 16:09:20 +0530 Subject: [PATCH 012/129] Auditors print formats --- .../doctype/journal_entry/journal_entry.py | 7 ++ .../doctype/payment_entry/payment_entry.py | 8 ++ .../purchase_invoice/purchase_invoice.py | 8 ++ .../doctype/sales_invoice/sales_invoice.py | 7 ++ .../bank_and_cash_payment_voucher/__init__.py | 0 .../bank_and_cash_payment_voucher.html | 82 +++++++++++++++ .../bank_and_cash_payment_voucher.json | 22 +++++ .../journal_auditing_voucher/__init__.py | 0 .../journal_auditing_voucher.html | 76 ++++++++++++++ .../journal_auditing_voucher.json | 22 +++++ .../purchase_auditing_voucher/__init__.py | 0 .../purchase_auditing_voucher.html | 99 +++++++++++++++++++ .../purchase_auditing_voucher.json | 22 +++++ .../sales_auditing_voucher/__init__.py | 0 .../sales_auditing_voucher.html | 93 +++++++++++++++++ .../sales_auditing_voucher.json | 22 +++++ 16 files changed, 468 insertions(+) create mode 100644 erpnext/accounts/print_format/bank_and_cash_payment_voucher/__init__.py create mode 100644 erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html create mode 100644 erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.json create mode 100644 erpnext/accounts/print_format/journal_auditing_voucher/__init__.py create mode 100644 erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html create mode 100644 erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.json create mode 100644 erpnext/accounts/print_format/purchase_auditing_voucher/__init__.py create mode 100644 erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html create mode 100644 erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.json create mode 100644 erpnext/accounts/print_format/sales_auditing_voucher/__init__.py create mode 100644 erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html create mode 100644 erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.json diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 259172e448..9813ba4ef5 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -52,6 +52,13 @@ class JournalEntry(AccountsController): self.update_loan() self.update_inter_company_jv() + def before_print(self): + gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Journal Entry", + "voucher_no": self.name} , + fields=["account", "party_type", "party", "debit", "credit", "remarks"] + ) + self.gl = gl_entries + def get_title(self): return self.pay_to_recd_from or self.accounts[0].account diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index f303301a33..6fc2e52981 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -70,6 +70,14 @@ class PaymentEntry(AccountsController): self.update_advance_paid() self.update_expense_claim() + def before_print(self): + gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Payment Entry", + "voucher_no": self.name} , + fields=["account", "party_type", "party", "debit", "credit", "remarks"] + ) + print(gl_entries) + self.gl = gl_entries + def on_cancel(self): self.setup_party_account_field() self.make_gl_entries(cancel=1) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 0dd716df3f..1163d760f6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -24,6 +24,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_ unlink_inter_company_invoice from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details from erpnext.accounts.deferred_revenue import validate_service_stop_date +from pprint import pprint form_grid_templates = { "items": "templates/form_grid/item_grid.html" @@ -53,6 +54,13 @@ class PurchaseInvoice(BuyingController): if not self.on_hold: self.release_date = '' + def before_print(self): + gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Purchase Invoice", + "voucher_no": self.name} , + fields=["account", "party_type", "party", "debit", "credit"] + ) + self.gl = gl_entries + def invoice_is_blocked(self): return self.on_hold and (not self.release_date or self.release_date > getdate(nowdate())) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 895ca07da2..5e747b3523 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -205,6 +205,13 @@ class SalesInvoice(SellingController): def before_cancel(self): self.update_time_sheet(None) + def before_print(self): + gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Sales Invoice", + "voucher_no": self.name} , + fields=["account", "party_type", "party", "debit", "credit"] + ) + self.gl = gl_entries + def on_cancel(self): self.check_close_sales_order("sales_order") diff --git a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/__init__.py b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html new file mode 100644 index 0000000000..7b1a8a2a1c --- /dev/null +++ b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html @@ -0,0 +1,82 @@ +{%- from "templates/print_formats/standard_macros.html" import add_header -%} + +
+ {%- if not doc.get("print_heading") and not doc.get("select_print_heading") + and doc.set("select_print_heading", _("Payment Entry")) -%}{%- endif -%} + {{ add_header(0, 1, doc, letter_head, no_letterhead, print_settings) }} +
+
+ + +
Voucher No: {{ doc.name }}
+
+
+ + +
Date: {{ frappe.utils.formatdate(doc.creation) }}
+
+
+
+ + + + + + + + + + + {% set total_credit = 0 -%} + {% for entries in doc.gl %} + {% if entries.debit == 0.0 %} + + + + + + {% set total_credit = total_credit + entries.credit -%} + + + + + + + + + {% endif %} + {% endfor %} + + + + + + + {% set total_debit = 0 -%} + {% for entries in doc.gl %} + {% if entries.credit == 0.0 %} + + + + + {% set total_debit = total_debit + entries.debit -%} + + + + + + + + + + {% endif %} + {% endfor %} +
AccountParty TypePartyAmount
Credit
{{ entries.account }}{{ entries.party_type }}{{ entries.party }}{{ entries.credit }}
Narration
{{ entries.remarks }}
Total (credit) {{total_credit}}
Debit
{{ entries.account }}{{ entries.party_type }}{{ entries.party }}{{ entries.debit }}
Narration
{{ entries.remarks }}
Total (debit) {{total_debit}}
+
+
\ No newline at end of file diff --git a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.json b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.json new file mode 100644 index 0000000000..e3afaec2ad --- /dev/null +++ b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.json @@ -0,0 +1,22 @@ +{ + "align_labels_right": 0, + "creation": "2019-02-15 11:49:08.608619", + "custom_format": 0, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Payment Entry", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "idx": 0, + "line_breaks": 0, + "modified": "2019-02-15 11:49:08.608619", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Bank and Cash Payment Voucher", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Server", + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/accounts/print_format/journal_auditing_voucher/__init__.py b/erpnext/accounts/print_format/journal_auditing_voucher/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html new file mode 100644 index 0000000000..cacb5f2a57 --- /dev/null +++ b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html @@ -0,0 +1,76 @@ +{%- from "templates/print_formats/standard_macros.html" import add_header -%} + +
+ {%- if not doc.get("print_heading") and not doc.get("select_print_heading") + and doc.set("select_print_heading", _("Journal Entry")) -%}{%- endif -%} + {{ add_header(0, 1, doc, letter_head, no_letterhead, print_settings) }} +
+
+ + +
Voucher No: {{ doc.name }}
+
+
+ + +
Date: {{ frappe.utils.formatdate(doc.creation) }}
+
+
+
+ + + + + + + + + + + {% set total_credit = 0 -%} + {% for entries in doc.gl %} + {% if entries.debit == 0.0 %} + + + + + + {% set total_credit = total_credit + entries.credit -%} + + + + + + {% endif %} + {% endfor %} + + + + + + + {% set total_debit = 0 -%} + {% for entries in doc.gl %} + {% if entries.credit == 0.0 %} + + + + + {% set total_debit = total_debit + entries.debit -%} + + + + + + + {% endif %} + {% endfor %} +
AccountParty TypePartyAmount
Credit
{{ entries.account }}{{ entries.party_type }}{{ entries.party }}{{ entries.credit }}
Total (credit) {{total_credit}}
Debit
{{ entries.account }}{{ entries.party_type }}{{ entries.party }}{{ entries.debit }}
Total (debit) {{total_debit}}
+
+
\ No newline at end of file diff --git a/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.json b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.json new file mode 100644 index 0000000000..927e818e01 --- /dev/null +++ b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.json @@ -0,0 +1,22 @@ +{ + "align_labels_right": 0, + "creation": "2019-02-15 14:13:05.721784", + "custom_format": 0, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Journal Entry", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "idx": 0, + "line_breaks": 0, + "modified": "2019-02-15 14:13:05.721784", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Journal Auditing Voucher", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Server", + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/accounts/print_format/purchase_auditing_voucher/__init__.py b/erpnext/accounts/print_format/purchase_auditing_voucher/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html new file mode 100644 index 0000000000..c8bd5c21ec --- /dev/null +++ b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html @@ -0,0 +1,99 @@ +{%- from "templates/print_formats/standard_macros.html" import add_header -%} +
+ {%- if not doc.get("print_heading") and not doc.get("select_print_heading") + and doc.set("select_print_heading", _("Purchase Invoice")) -%}{%- endif -%} + {{ add_header(0, 1, doc, letter_head, no_letterhead, print_settings) }} +
+
+ + + + + + +
Supplier Name: {{ doc.supplier }}
Due Date: {{ frappe.utils.formatdate(doc.due_date) }}
Address: {{doc.address_display}}
Contact: {{doc.contact_display}}
Mobile no: {{doc.contact_mobile}}
+
+
+ + + +
Voucher No: {{ doc.name }}
Date: {{ frappe.utils.formatdate(doc.creation) }}
+
+
+
+ + + + + + + + + + + + + {% for item in doc.items %} + + + + + + + + + + + + {% endfor %} +
SLItem CodeItem NameUOMReceived Qty.Rejected QtyQtyBasic RateAmount
{{ loop.index }}{{ item.item_code }}{{ item.item_name }}{{ item.uom }}{{ item.received_qty }}{{ item.rejected_qty }}{{ item.qty}}{{ item.rate }}{{ item.amount }}
+
+
+
+ + + + +
Total Quantity: {{ doc.total_qty }}
Total: {{doc.total}}
Net Weight: {{ doc.total_net_weight }}
+
+
+ + + {% for tax in doc.taxes %} + + {% endfor %} + + + + +
Tax and Charges: {{doc.taxes_and_charges}}
{{ tax.account_head }}: {{ tax.tax_amount_after_discount_amount }}
Taxes and Charges Added: {{ doc.taxes_and_charges_added }}
Taxes and Charges Deducted: {{ doc.taxes_and_charges_deducted }}
Total Taxes and Charges: {{ doc.total_taxes_and_charges }}
Net Payable: {{ doc.grand_total }}
+
+
+
+ + + + + + + + + + {% for entries in doc.gl %} + + + + + + + + + {% endfor %} + + + + + +
SLAccountParty TypePartyCredit AmountDebit Amount
{{ loop.index }}{{ entries.account }}{{ entries.party_type }}{{ entries.party }}{{ entries.credit }}{{ entries.debit }}
Total{{ doc.grand_total|flt }}{{ doc.grand_total|flt }}
+
+
\ No newline at end of file diff --git a/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.json b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.json new file mode 100644 index 0000000000..73779d49aa --- /dev/null +++ b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.json @@ -0,0 +1,22 @@ +{ + "align_labels_right": 0, + "creation": "2019-02-14 14:42:35.151611", + "custom_format": 0, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Purchase Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "idx": 0, + "line_breaks": 0, + "modified": "2019-02-14 14:42:35.151611", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Purchase Auditing Voucher", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Server", + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/accounts/print_format/sales_auditing_voucher/__init__.py b/erpnext/accounts/print_format/sales_auditing_voucher/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html new file mode 100644 index 0000000000..b3ce888fa5 --- /dev/null +++ b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html @@ -0,0 +1,93 @@ +{%- from "templates/print_formats/standard_macros.html" import add_header -%} +
+ {%- if not doc.get("print_heading") and not doc.get("select_print_heading") + and doc.set("select_print_heading", _("Sales Invoice")) -%}{%- endif -%} + {{ add_header(0, 1, doc, letter_head, no_letterhead, print_settings) }} +
+
+ + + + + + +
Customer Name: {{ doc.customer }}
Due Date: {{ frappe.utils.formatdate(doc.due_date) }}
Address: {{doc.address_display}}
Contact: {{doc.contact_display}}
Mobile no: {{doc.contact_mobile}}
+
+
+ + + +
Voucher No: {{ doc.name }}
Date: {{ frappe.utils.formatdate(doc.creation) }}
+
+
+
+ + + + + + + + + + + {% for item in doc.items %} + + + + + + + + + + {% endfor %} +
SLItem CodeItem NameUOMQuantityBasic RateAmount
{{ loop.index }}{{ item.item_code }}{{ item.item_name }}{{ item.uom }}{{ item.qty}}{{ item.rate }}{{ item.amount }}
+
+
+
+ + + + +
Total Quantity: {{ doc.total_qty }}
Total: {{doc.total}}
Net Weight: {{ doc.total_net_weight }}
+
+
+ + + {% for tax in doc.taxes %} + + {% endfor %} + + +
Tax and Charges: {{doc.taxes_and_charges}}
{{ tax.account_head }}: {{ tax.tax_amount_after_discount_amount }}
Total Taxes and Charges: {{ doc.total_taxes_and_charges }}
Net Payable: {{ doc.grand_total }}
+
+
+
+ + + + + + + + + + {% for entries in doc.gl %} + + + + + + + + + {% endfor %} + + + + + +
SLAccountParty TypePartyCredit AmountDebit Amount
{{ loop.index }}{{ entries.account }}{{ entries.party_type }}{{ entries.party }}{{ entries.credit }}{{ entries.debit }}
Total{{ doc.grand_total|flt }}{{ doc.grand_total|flt }}
+
+
\ No newline at end of file diff --git a/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.json b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.json new file mode 100644 index 0000000000..0544e0bc9e --- /dev/null +++ b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.json @@ -0,0 +1,22 @@ +{ + "align_labels_right": 0, + "creation": "2019-02-15 15:02:51.454754", + "custom_format": 0, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Sales Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "idx": 0, + "line_breaks": 0, + "modified": "2019-02-15 15:02:51.454754", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Sales Auditing Voucher", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Server", + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file From 879e5fd8bbfa8ee331a3603241077cf1c63207b7 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 18 Feb 2019 14:43:55 +0530 Subject: [PATCH 013/129] remove print and codacy --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 1 - erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 1 - 2 files changed, 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 6fc2e52981..ef4cd3d31d 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -75,7 +75,6 @@ class PaymentEntry(AccountsController): "voucher_no": self.name} , fields=["account", "party_type", "party", "debit", "credit", "remarks"] ) - print(gl_entries) self.gl = gl_entries def on_cancel(self): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 1163d760f6..9ac532dd69 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -24,7 +24,6 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_ unlink_inter_company_invoice from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details from erpnext.accounts.deferred_revenue import validate_service_stop_date -from pprint import pprint form_grid_templates = { "items": "templates/form_grid/item_grid.html" From 1ac17ec1c4e402d384200954a0dc077d8083c662 Mon Sep 17 00:00:00 2001 From: Jigar Tarpara Date: Tue, 19 Feb 2019 10:21:01 +0530 Subject: [PATCH 014/129] Add credit month in validation --- .../doctype/payment_terms_template/payment_terms_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py index 7042df0594..2b2b6afe79 100644 --- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py +++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py @@ -32,7 +32,7 @@ class PaymentTermsTemplate(Document): def check_duplicate_terms(self): terms = [] for term in self.terms: - term_info = (term.credit_days, term.due_date_based_on) + term_info = (term.credit_days, term.credit_months, term.due_date_based_on) if term_info in terms: frappe.msgprint( _('The Payment Term at row {0} is possibly a duplicate.').format(term.idx), From 143973166fb9fa1f2a74f6755ba8081e5cbf196a Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Thu, 21 Feb 2019 15:32:52 +0530 Subject: [PATCH 015/129] fix(payments): Only check for Draft or Submitted paymentswhile cancelling payment requests --- .../accounts/doctype/payment_request/payment_request.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 014efd948c..1fbde753d4 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -214,9 +214,11 @@ class PaymentRequest(Document): def check_if_payment_entry_exists(self): if self.status == "Paid": - payment_entry = frappe.db.sql_list("""select parent from `tabPayment Entry Reference` - where reference_name=%s""", self.reference_name) - if payment_entry: + payment_entry = frappe.get_all("Payment Entry Reference", + filters={"reference_name": self.reference_name, "docstatus": ["<", 2]}, + fields=["distinct(parent)"]) + + if any(payment_entry): frappe.throw(_("Payment Entry already exists"), title=_('Error')) def make_communication_entry(self): From a9a1552e328e0c4155d72b8e4ff665d116c7a3d4 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 27 Feb 2019 14:10:24 +0530 Subject: [PATCH 016/129] refactor --- .../gross_and_net_profit_report/gross_and_net_profit_report.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py index fe767c0409..739c4f0051 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -69,6 +69,7 @@ def get_revenue(data, period_list, revenue_type): revenue = [item for item in data if item['include_in_gross']==1 or item['is_group']==1] elif revenue_type == 'non_gross': revenue = [item for item in data if item['include_in_gross']==0 or item['is_group']==1] + revenue, status = remove_parent_with_no_child(revenue, period_list) while status == "data to be removed": revenue, status = remove_parent_with_no_child(revenue, period_list) From ab4ff984c4a8e19cc5173b36e9eff56c2ef91373 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 28 Feb 2019 13:21:28 +0530 Subject: [PATCH 017/129] fix: scan barcode not adding the barcode value in the items table --- erpnext/public/js/controllers/transaction.js | 21 +++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index cf62af7b70..168a727f27 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -314,14 +314,21 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ show_description(row_to_modify.idx, row_to_modify.item_code); + this.frm.from_barcode = true; frappe.model.set_value(row_to_modify.doctype, row_to_modify.name, { item_code: data.item_code, qty: (row_to_modify.qty || 0) + 1 }); - this.frm.refresh_field('items'); + ['serial_no', 'batch_no', 'barcode'].forEach(field => { + if (data[field] && frappe.meta.has_field(row_to_modify.doctype, field)) { + frappe.model.set_value(row_to_modify.doctype, + row_to_modify.name, field, data[field]); + } + }); + + scan_barcode_field.set_value(''); }); - scan_barcode_field.set_value(''); } return false; }, @@ -384,10 +391,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ // barcode cleared, remove item d.item_code = ""; } - this.item_code(doc, cdt, cdn, true); + + this.frm.from_barcode = true; + this.item_code(doc, cdt, cdn); }, - item_code: function(doc, cdt, cdn, from_barcode) { + item_code: function(doc, cdt, cdn) { var me = this; var item = frappe.get_doc(cdt, cdn); var update_stock = 0, show_batch_dialog = 0; @@ -400,9 +409,11 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ show_batch_dialog = 1; } // clear barcode if setting item (else barcode will take priority) - if(!from_barcode) { + if(!this.frm.from_barcode) { item.barcode = null; } + + this.frm.from_barcode = false; if(item.item_code || item.barcode || item.serial_no) { if(!this.validate_company_and_party()) { this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove(); From 6635583103840dbdcca52e2552417fc3d2ca0bae Mon Sep 17 00:00:00 2001 From: "FinByz Tech Pvt. Ltd" Date: Fri, 1 Mar 2019 11:57:06 +0530 Subject: [PATCH 018/129] fix: Rounding adjustment in Rounded Total In Purchase Receipt and GST Purchase Receipt report, the Rounded Total column doesn't display rounding adjustment of Base Grand Total. So, we have created a fix for that rounding adjustment. --- .../accounts/report/purchase_register/purchase_register.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index e33b90d019..3f8abb76e2 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -66,8 +66,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum total_tax += tax_amount row.append(tax_amount) - # total tax, grand total, outstanding amount & rounded total - row += [total_tax, inv.base_grand_total, flt(inv.base_grand_total, 2), inv.outstanding_amount] + # total tax, grand total, rounded total & outstanding amount + row += [total_tax, inv.base_grand_total, flt(inv.base_grand_total, 0), inv.outstanding_amount] data.append(row) return columns, data From 4ef924d0ba5e7c0cc990c9b9df603fa58fbf4bc0 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 1 Mar 2019 16:24:54 +0530 Subject: [PATCH 019/129] fix: on save state code field become blank --- erpnext/regional/italy/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py index 30280e20ad..f39b144cdc 100644 --- a/erpnext/regional/italy/utils.py +++ b/erpnext/regional/italy/utils.py @@ -343,4 +343,7 @@ def set_state_code(doc, method): return state_codes_lower = {key.lower():value for key,value in state_codes.items()} - doc.state_code = state_codes_lower.get(doc.get('state','').lower()) + + state = doc.get('state','').lower() + if state_codes_lower.get(state): + doc.state_code = state_codes_lower.get(state) From c2090939d74b5b3b52df70a8190550448315e25b Mon Sep 17 00:00:00 2001 From: Jay Parikh Date: Sat, 2 Mar 2019 12:19:32 +0000 Subject: [PATCH 020/129] Fix Sales Invoice Return Validation "validate_pos" --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 4cf3a1acf5..10a9dc6da5 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -523,8 +523,8 @@ class SalesInvoice(SellingController): def validate_pos(self): if self.is_return: - if flt(self.paid_amount) + flt(self.write_off_amount) - flt(self.grand_total) < \ - 1/(10**(self.precision("grand_total") + 1)): + if flt(self.paid_amount) + flt(self.write_off_amount) - flt(self.grand_total) > \ + 1.0/(10.0**(self.precision("grand_total") + 1.0)): frappe.throw(_("Paid amount + Write Off Amount can not be greater than Grand Total")) def validate_item_code(self): From 2bfb063fdb9ee4576b2285dd49ccd4c85d6b0bd5 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 2 Mar 2019 21:47:55 +0530 Subject: [PATCH 021/129] fix(sales order item): not able to search item by description --- erpnext/controllers/queries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 4c16323ca3..f8f1e54459 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -172,8 +172,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals or tabItem.item_code LIKE %(txt)s or tabItem.item_group LIKE %(txt)s or tabItem.item_name LIKE %(txt)s - or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s - {description_cond})) + or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s) + {description_cond}) {fcond} {mcond} order by if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), From f6d1f53aeff1f4cb31570f0f88f4a6d02d0a3821 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 4 Mar 2019 10:23:35 +0530 Subject: [PATCH 022/129] fix: Account type fix in test_united_states --- erpnext/regional/united_states/test_united_states.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/united_states/test_united_states.py b/erpnext/regional/united_states/test_united_states.py index 3c768b7b0b..df6cb395b0 100644 --- a/erpnext/regional/united_states/test_united_states.py +++ b/erpnext/regional/united_states/test_united_states.py @@ -38,7 +38,7 @@ def make_payment_entry_to_irs_1099_supplier(): pe.company = "_Test Company" pe.posting_date = "2016-01-10" pe.paid_from = "_Test Bank USD - _TC" - pe.paid_to = "_Test Bank - _TC" + pe.paid_to = "_Test Payable USD - _TC" pe.paid_amount = 100 pe.received_amount = 100 pe.reference_no = "For IRS 1099 testing" From f3bdeedc686e25e7b43913fa271e7dc42bbd3b6c Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 4 Mar 2019 12:27:41 +0530 Subject: [PATCH 023/129] minor fixes --- .../gross_and_net_profit_report.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py index 739c4f0051..a0432dbef3 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -79,10 +79,10 @@ def get_revenue(data, period_list, revenue_type): def remove_parent_with_no_child(data, period_list): status = "nothing to remove" for parent in data: - if 'is_group' in parent and parent["is_group"] == 1: + if 'is_group' in parent and parent.get("is_group") == 1: have_child = False for child in data: - if 'parent_account' in child and child["parent_account"] == parent["account"]: + if 'parent_account' in child and child.get("parent_account") == parent.get("account"): have_child = True break @@ -116,8 +116,6 @@ def set_total(node, value, complete_list, totals): def get_profit(gross_income, gross_expense, period_list, company, profit_type, currency=None, consolidated=False): - total = 0 - profit_loss = { "account_name": "'" + _(profit_type) + "'", "account": "'" + _(profit_type) + "'", @@ -134,14 +132,10 @@ def get_profit(gross_income, gross_expense, period_list, company, profit_type, c if profit_loss[key]: has_value=True - total += flt(profit_loss[key]) - profit_loss['total'] = total - if has_value: return profit_loss def get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expense, period_list, company, currency=None, consolidated=False): - total = 0 profit_loss = { "account_name": "'" + _("Net Profit") + "'", "account": "'" + _("Net Profit") + "'", @@ -160,8 +154,5 @@ def get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expe if profit_loss[key]: has_value=True - total += flt(profit_loss[key]) - profit_loss['total'] = total - if has_value: return profit_loss From 1e4e61bd948463caac12f302a0896753421f6517 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 4 Mar 2019 12:36:51 +0530 Subject: [PATCH 024/129] Minor fixes --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 3 +-- erpnext/accounts/doctype/payment_entry/payment_entry.py | 3 +-- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 3 +-- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 9813ba4ef5..27c946ddc1 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -53,11 +53,10 @@ class JournalEntry(AccountsController): self.update_inter_company_jv() def before_print(self): - gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Journal Entry", + self.gl = frappe.get_list("GL Entry",filters={"voucher_type": "Journal Entry", "voucher_no": self.name} , fields=["account", "party_type", "party", "debit", "credit", "remarks"] ) - self.gl = gl_entries def get_title(self): return self.pay_to_recd_from or self.accounts[0].account diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index ef4cd3d31d..00ffd17234 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -71,11 +71,10 @@ class PaymentEntry(AccountsController): self.update_expense_claim() def before_print(self): - gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Payment Entry", + self.gl = frappe.get_list("GL Entry",filters={"voucher_type": "Payment Entry", "voucher_no": self.name} , fields=["account", "party_type", "party", "debit", "credit", "remarks"] ) - self.gl = gl_entries def on_cancel(self): self.setup_party_account_field() diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 9ac532dd69..cbade186ad 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -54,11 +54,10 @@ class PurchaseInvoice(BuyingController): self.release_date = '' def before_print(self): - gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Purchase Invoice", + self.gl = frappe.get_list("GL Entry",filters={"voucher_type": "Purchase Invoice", "voucher_no": self.name} , fields=["account", "party_type", "party", "debit", "credit"] ) - self.gl = gl_entries def invoice_is_blocked(self): return self.on_hold and (not self.release_date or self.release_date > getdate(nowdate())) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 5e747b3523..5bc127239a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -206,11 +206,10 @@ class SalesInvoice(SellingController): self.update_time_sheet(None) def before_print(self): - gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Sales Invoice", + self.gl = frappe.get_list("GL Entry",filters={"voucher_type": "Sales Invoice", "voucher_no": self.name} , fields=["account", "party_type", "party", "debit", "credit"] ) - self.gl = gl_entries def on_cancel(self): self.check_close_sales_order("sales_order") From e1f72cc010def067a2caf83939621129b01ff77e Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 4 Mar 2019 12:49:39 +0530 Subject: [PATCH 025/129] revert: Address and contact report fix (#16786) Reverts #16674 Address and other info are not visible in address and contact report ![screenshot 2019-02-27 at 11 31 38 am](https://user-images.githubusercontent.com/42651287/53469309-6029f500-3a83-11e9-9672-e5e7d14dc470.png) --- .../report/address_and_contacts/address_and_contacts.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/selling/report/address_and_contacts/address_and_contacts.py b/erpnext/selling/report/address_and_contacts/address_and_contacts.py index eb242d0a73..a9e43034b4 100644 --- a/erpnext/selling/report/address_and_contacts/address_and_contacts.py +++ b/erpnext/selling/report/address_and_contacts/address_and_contacts.py @@ -102,8 +102,7 @@ def get_party_details(party_type, party_list, doctype, party_details): records = frappe.get_list(doctype, filters=filters, fields=fields, as_list=True) for d in records: details = party_details.get(d[0]) - if details: - details.setdefault(frappe.scrub(doctype), []).append(d[1:]) + details.setdefault(frappe.scrub(doctype), []).append(d[1:]) return party_details From 40743840b7a85da4f8966703eed981b2c82a4aad Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 4 Mar 2019 12:53:11 +0530 Subject: [PATCH 026/129] fix: Print letter head only if checked in Print Settings (#16789) Letter head is printed in financial statements even if option is not checked ![letter head option](https://user-images.githubusercontent.com/42651287/53430759-26baa080-3a15-11e9-948a-bc95700a18d2.png) --- erpnext/accounts/report/financial_statements.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/financial_statements.html b/erpnext/accounts/report/financial_statements.html index 4b3b5f2d53..449fb845d7 100644 --- a/erpnext/accounts/report/financial_statements.html +++ b/erpnext/accounts/report/financial_statements.html @@ -15,7 +15,7 @@ height: 37px; } -{% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) || frappe.defaults.get_default("letter_head"); %} +{% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) %} {% if(letterhead) { %}
{%= frappe.boot.letter_heads[letterhead].header %} From 44ff41188d85e9a7dd50e96329496cca42707576 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 4 Mar 2019 12:56:27 +0530 Subject: [PATCH 027/129] fix: Selling and buying amount precision fix (#16764) --- .../report/gross_profit/gross_profit.py | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 67105e58de..073516fb88 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -6,7 +6,7 @@ import frappe from frappe import _, scrub from erpnext.stock.utils import get_incoming_rate from erpnext.controllers.queries import get_match_cond -from frappe.utils import flt +from frappe.utils import flt, cint def execute(filters=None): @@ -106,11 +106,14 @@ class GrossProfitGenerator(object): self.grouped = {} self.grouped_data = [] + self.currency_precision = cint(frappe.db.get_default("currency_precision")) or 3 + self.float_precision = cint(frappe.db.get_default("float_precision")) or 2 + for row in self.si_list: if self.skip_row(row, self.product_bundles): continue - row.base_amount = flt(row.base_net_amount) + row.base_amount = flt(row.base_net_amount, self.currency_precision) product_bundles = [] if row.update_stock: @@ -129,15 +132,15 @@ class GrossProfitGenerator(object): # get buying rate if row.qty: - row.buying_rate = row.buying_amount / row.qty - row.base_rate = row.base_amount / row.qty + row.buying_rate = flt(row.buying_amount / row.qty, self.float_precision) + row.base_rate = flt(row.base_amount / row.qty, self.float_precision) else: row.buying_rate, row.base_rate = 0.0, 0.0 # calculate gross profit - row.gross_profit = flt(row.base_amount - row.buying_amount, 3) + row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision) if row.base_amount: - row.gross_profit_percent = flt((row.gross_profit / row.base_amount) * 100.0, 3) + row.gross_profit_percent = flt((row.gross_profit / row.base_amount) * 100.0, self.currency_precision) else: row.gross_profit_percent = 0.0 @@ -156,8 +159,8 @@ class GrossProfitGenerator(object): new_row = row else: new_row.qty += row.qty - new_row.buying_amount += row.buying_amount - new_row.base_amount += row.base_amount + new_row.buying_amount += flt(row.buying_amount, self.currency_precision) + new_row.base_amount += flt(row.base_amount, self.currency_precision) new_row = self.set_average_rate(new_row) self.grouped_data.append(new_row) else: @@ -167,18 +170,19 @@ class GrossProfitGenerator(object): returned_item_rows = self.returned_invoices[row.parent][row.item_code] for returned_item_row in returned_item_rows: row.qty += returned_item_row.qty - row.base_amount += returned_item_row.base_amount - row.buying_amount = row.qty * row.buying_rate + row.base_amount += flt(returned_item_row.base_amount, self.currency_precision) + row.buying_amount = flt(row.qty * row.buying_rate, self.currency_precision) if row.qty or row.base_amount: row = self.set_average_rate(row) self.grouped_data.append(row) def set_average_rate(self, new_row): - new_row.gross_profit = flt(new_row.base_amount - new_row.buying_amount,3) - new_row.gross_profit_percent = flt(((new_row.gross_profit / new_row.base_amount) * 100.0),3) \ + new_row.gross_profit = flt(new_row.base_amount - new_row.buying_amount, self.currency_precision) + new_row.gross_profit_percent = flt(((new_row.gross_profit / new_row.base_amount) * 100.0), self.currency_precision) \ if new_row.base_amount else 0 - new_row.buying_rate = (new_row.buying_amount / new_row.qty) if new_row.qty else 0 - new_row.base_rate = (new_row.base_amount / new_row.qty) if new_row.qty else 0 + new_row.buying_rate = flt(new_row.buying_amount / new_row.qty, self.float_precision) if new_row.qty else 0 + new_row.base_rate = flt(new_row.base_amount / new_row.qty, self.float_precision) if new_row.qty else 0 + return new_row def get_returned_invoice_items(self): From e615051ccc0096941fbe908deb1fcefc32f41687 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 4 Mar 2019 13:22:51 +0530 Subject: [PATCH 028/129] fix: Updated test_united_states --- erpnext/regional/report/irs_1099/irs_1099.py | 2 +- .../united_states/test_united_states.py | 35 +++++++++++-------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/erpnext/regional/report/irs_1099/irs_1099.py b/erpnext/regional/report/irs_1099/irs_1099.py index cef8950402..0cb4a3a360 100644 --- a/erpnext/regional/report/irs_1099/irs_1099.py +++ b/erpnext/regional/report/irs_1099/irs_1099.py @@ -26,7 +26,7 @@ def execute(filters=None): s.supplier_group as "supplier_group", gl.party AS "supplier", s.tax_id as "tax_id", - SUM(gl.debit) AS "payments" + SUM(gl.debit_in_account_currency) AS "payments" FROM `tabGL Entry` gl INNER JOIN `tabSupplier` s WHERE diff --git a/erpnext/regional/united_states/test_united_states.py b/erpnext/regional/united_states/test_united_states.py index df6cb395b0..7634843b08 100644 --- a/erpnext/regional/united_states/test_united_states.py +++ b/erpnext/regional/united_states/test_united_states.py @@ -8,28 +8,34 @@ from erpnext.regional.report.irs_1099.irs_1099 import execute as execute_1099_re class TestUnitedStates(unittest.TestCase): def test_irs_1099_custom_field(self): - doc = frappe.new_doc("Supplier") - doc.supplier_name = "_US 1099 Test Supplier" - doc.supplier_group = "Services" - doc.supplier_type = "Company" - doc.country = "United States" - doc.tax_id = "04-1234567" - doc.irs_1099 = 1 - doc.save() - frappe.db.commit() - supplier = frappe.get_doc('Supplier', "_US 1099 Test Supplier") - self.assertEqual(supplier.irs_1099, 1) + + frappe.db.sql("delete from `tabGL Entry` where party='_US 1099 Test Supplier'") + frappe.db.sql("delete from `tabGL Entry` where against='_US 1099 Test Supplier'") + frappe.db.sql("delete from `tabPayment Entry` where party='_US 1099 Test Supplier'") + + if not frappe.db.exists("Supplier", "_US 1099 Test Supplier"): + doc = frappe.new_doc("Supplier") + doc.supplier_name = "_US 1099 Test Supplier" + doc.supplier_group = "Services" + doc.supplier_type = "Company" + doc.country = "United States" + doc.tax_id = "04-1234567" + doc.irs_1099 = 1 + doc.save() + frappe.db.commit() + supplier = frappe.get_doc('Supplier', "_US 1099 Test Supplier") + self.assertEqual(supplier.irs_1099, 1) def test_irs_1099_report(self): make_payment_entry_to_irs_1099_supplier() - filters = frappe._dict({"fiscal_year": "2016", "company": "_Test Company"}) + filters = frappe._dict({"fiscal_year": "_Test Fiscal Year 2016", "company": "_Test Company"}) columns, data = execute_1099_report(filters) print(columns, data) expected_row = {'supplier': '_US 1099 Test Supplier', 'supplier_group': 'Services', 'payments': 100.0, 'tax_id': '04-1234567'} - self.assertEqual(data, expected_row) + self.assertEqual(data[0], expected_row) def make_payment_entry_to_irs_1099_supplier(): @@ -45,4 +51,5 @@ def make_payment_entry_to_irs_1099_supplier(): pe.reference_date = "2016-01-10" pe.party_type = "Supplier" pe.party = "_US 1099 Test Supplier" - pe.save() + pe.insert() + pe.submit() From 511713e131e89efb39dcf2a40b26866efd974acb Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 4 Mar 2019 13:25:39 +0530 Subject: [PATCH 029/129] fix: Delete GL entry for supplier before creating new one --- erpnext/regional/united_states/test_united_states.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/regional/united_states/test_united_states.py b/erpnext/regional/united_states/test_united_states.py index 7634843b08..688f14576c 100644 --- a/erpnext/regional/united_states/test_united_states.py +++ b/erpnext/regional/united_states/test_united_states.py @@ -9,10 +9,6 @@ from erpnext.regional.report.irs_1099.irs_1099 import execute as execute_1099_re class TestUnitedStates(unittest.TestCase): def test_irs_1099_custom_field(self): - frappe.db.sql("delete from `tabGL Entry` where party='_US 1099 Test Supplier'") - frappe.db.sql("delete from `tabGL Entry` where against='_US 1099 Test Supplier'") - frappe.db.sql("delete from `tabPayment Entry` where party='_US 1099 Test Supplier'") - if not frappe.db.exists("Supplier", "_US 1099 Test Supplier"): doc = frappe.new_doc("Supplier") doc.supplier_name = "_US 1099 Test Supplier" @@ -39,6 +35,11 @@ class TestUnitedStates(unittest.TestCase): def make_payment_entry_to_irs_1099_supplier(): + + frappe.db.sql("delete from `tabGL Entry` where party='_US 1099 Test Supplier'") + frappe.db.sql("delete from `tabGL Entry` where against='_US 1099 Test Supplier'") + frappe.db.sql("delete from `tabPayment Entry` where party='_US 1099 Test Supplier'") + pe = frappe.new_doc("Payment Entry") pe.payment_type = "Pay" pe.company = "_Test Company" From 9842ce5a795808a63d2cb232bbba385a74df5e99 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 4 Mar 2019 13:39:52 +0530 Subject: [PATCH 030/129] fix: HSN code not disaplying for the GST Purchase Invoice print format --- .../gst_purchase_invoice/gst_purchase_invoice.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/print_format/gst_purchase_invoice/gst_purchase_invoice.json b/erpnext/accounts/print_format/gst_purchase_invoice/gst_purchase_invoice.json index 2b7f9ce757..6d7c3d31ae 100644 --- a/erpnext/accounts/print_format/gst_purchase_invoice/gst_purchase_invoice.json +++ b/erpnext/accounts/print_format/gst_purchase_invoice/gst_purchase_invoice.json @@ -7,10 +7,10 @@ "docstatus": 0, "doctype": "Print Format", "font": "Default", - "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"
\\t\\t\\t\\t

Purchase Invoice
{{ doc.name }}\\t\\t\\t\\t

\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"supplier_name\", \"label\": \"Supplier Name\"}, {\"print_hide\": 0, \"fieldname\": \"due_date\", \"label\": \"Due Date\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Address\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"address_display\", \"label\": \"Address\"}, {\"print_hide\": 0, \"fieldname\": \"contact_display\", \"label\": \"Contact\"}, {\"print_hide\": 0, \"fieldname\": \"contact_mobile\", \"label\": \"Mobile No\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item_name\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"image\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"received_qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rejected_qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"discount_percentage\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"pricing_rule\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"weight_per_unit\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"total_weight\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"weight_uom\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"warehouse\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rejected_warehouse\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"batch_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"serial_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"bom\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"asset\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"items\", \"label\": \"Items\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"label\": \"Total\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"category\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"add_deduct_tax\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"charge_type\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"row_id\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"included_in_print_rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"account_head\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"cost_center\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"taxes\", \"label\": \"Purchase Taxes and Charges\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"grand_total\", \"label\": \"Grand Total\"}, {\"print_hide\": 0, \"fieldname\": \"in_words\", \"label\": \"In Words\"}, {\"print_hide\": 0, \"fieldname\": \"disable_rounded_total\", \"label\": \"Disable Rounded Total\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Payments\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"cash_bank_account\", \"label\": \"Cash/Bank Account\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"other_charges_calculation\", \"align\": \"left\", \"label\": \"Tax Breakup\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Raw\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"main_item_code\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rm_item_code\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"batch_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"serial_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"required_qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"consumed_qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"stock_uom\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"conversion_factor\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"current_stock\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"reference_name\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"bom_detail_no\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"supplied_items\", \"label\": \"Supplied Items\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Terms\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"terms\", \"label\": \"Terms and Conditions1\"}]", + "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"
\\t\\t\\t\\t

Purchase Invoice
{{ doc.name }}\\t\\t\\t\\t

\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"supplier_name\", \"label\": \"Supplier Name\"}, {\"print_hide\": 0, \"fieldname\": \"due_date\", \"label\": \"Due Date\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Address\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"address_display\", \"label\": \"Address\"}, {\"print_hide\": 0, \"fieldname\": \"contact_display\", \"label\": \"Contact\"}, {\"print_hide\": 0, \"fieldname\": \"contact_mobile\", \"label\": \"Mobile No\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item_name\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"image\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"received_qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rejected_qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"discount_percentage\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"pricing_rule\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"weight_per_unit\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"total_weight\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"weight_uom\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"warehouse\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rejected_warehouse\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"batch_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"serial_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"bom\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"asset\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"gst_hsn_code\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"items\", \"label\": \"Items\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"label\": \"Total\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"category\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"add_deduct_tax\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"charge_type\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"row_id\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"included_in_print_rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"account_head\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"cost_center\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"taxes\", \"label\": \"Purchase Taxes and Charges\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"grand_total\", \"label\": \"Grand Total\"}, {\"print_hide\": 0, \"fieldname\": \"in_words\", \"label\": \"In Words\"}, {\"print_hide\": 0, \"fieldname\": \"disable_rounded_total\", \"label\": \"Disable Rounded Total\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Payments\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"cash_bank_account\", \"label\": \"Cash/Bank Account\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"other_charges_calculation\", \"align\": \"left\", \"label\": \"Tax Breakup\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Raw\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"main_item_code\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rm_item_code\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"batch_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"serial_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"required_qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"consumed_qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"stock_uom\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"conversion_factor\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"current_stock\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"reference_name\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"bom_detail_no\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"supplied_items\", \"label\": \"Supplied Items\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Terms\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"terms\", \"label\": \"Terms and Conditions1\"}]", "idx": 0, "line_breaks": 0, - "modified": "2018-04-07 13:06:08.060353", + "modified": "2019-03-04 13:38:47.362002", "modified_by": "Administrator", "module": "Accounts", "name": "GST Purchase Invoice", From 8aa4594b1ec4be8c13259d2603129170260ac4b6 Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Mon, 4 Mar 2019 14:00:02 +0530 Subject: [PATCH 031/129] feat(modules): add onboard keys, remove hidden --- erpnext/config/agriculture.py | 7 ++++++- erpnext/config/buying.py | 2 +- erpnext/config/desktop.py | 38 +++++++++++++++-------------------- erpnext/config/education.py | 15 +++++++++----- erpnext/config/healthcare.py | 4 ++++ erpnext/config/non_profit.py | 4 ++++ erpnext/config/selling.py | 2 +- erpnext/config/settings.py | 2 +- 8 files changed, 43 insertions(+), 31 deletions(-) diff --git a/erpnext/config/agriculture.py b/erpnext/config/agriculture.py index a6bc3028d8..937d76ef7b 100644 --- a/erpnext/config/agriculture.py +++ b/erpnext/config/agriculture.py @@ -9,14 +9,17 @@ def get_data(): { "type": "doctype", "name": "Crop", + "onboard": 1, }, { "type": "doctype", "name": "Crop Cycle", + "onboard": 1, }, { "type": "doctype", - "name": "Location" + "name": "Location", + "onboard": 1, } ] }, @@ -26,10 +29,12 @@ def get_data(): { "type": "doctype", "name": "Disease", + "onboard": 1, }, { "type": "doctype", "name": "Fertilizer", + "onboard": 1, } ] }, diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index 2b116d22b4..264f88f9c1 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -84,7 +84,7 @@ def get_data(): { "type": "doctype", "name": "Buying Settings", - "onboard": 1, + "settings": 1, "description": _("Default settings for buying transactions.") }, { diff --git a/erpnext/config/desktop.py b/erpnext/config/desktop.py index 5d4dfa1687..48418f1d16 100644 --- a/erpnext/config/desktop.py +++ b/erpnext/config/desktop.py @@ -13,8 +13,10 @@ def get_data(): "color": "#1abc9c", "icon": "fa fa-check-square-o", "type": "module", - "hidden": 1, - "description": "Dive into the basics for your organisation's needs." + "disable_after_onboard": 1, + "shortcuts": ["Import Data", "Letter Head", "Email Account", "Item", "Customer"], + "description": "Dive into the basics for your organisation's needs.", + "onboard_present": 1 }, { "module_name": "Accounting", @@ -23,7 +25,7 @@ def get_data(): "color": "#3498db", "icon": "octicon octicon-repo", "type": "module", - "hidden": 1, + "shortcuts": ["Item", "Customer", "Supplier", "General Ledger", "Sales Invoice"], "description": "Accounts, billing, payments, cost center and budgeting." }, { @@ -33,7 +35,7 @@ def get_data(): "color": "#1abc9c", "icon": "octicon octicon-tag", "type": "module", - "hidden": 1, + "shortcuts": ["Quotation", "Sales Order", "Sales Analytics", "Customer"], "description": "Sales orders, quotations, customers and items." }, { @@ -43,7 +45,7 @@ def get_data(): "color": "#c0392b", "icon": "octicon octicon-briefcase", "type": "module", - "hidden": 1, + "shortcuts": ["Purchase Order", "Items and Pricing", "Settings", "Supplier"], "description": "Purchasing, suppliers, material requests, and items." }, { @@ -53,7 +55,7 @@ def get_data(): "color": "#f39c12", "icon": "octicon octicon-package", "type": "module", - "hidden": 1, + "shortcuts": ["Transactions", "Stock Reports", "Settings"], "description": "Stock transactions, reports, serial numbers and batches." }, { @@ -62,8 +64,8 @@ def get_data(): "label": _("Assets"), "color": "#4286f4", "icon": "octicon octicon-database", - "hidden": 1, "type": "module", + "shortcuts": ["Assets", "Maintanence", "Reports"], "description": "Asset movement, maintainance and tools." }, { @@ -73,7 +75,7 @@ def get_data(): "color": "#8e44ad", "icon": "octicon octicon-rocket", "type": "module", - "hidden": 1, + "shortcuts": ["Projects", "Time Tracking", "Reports"], "description": "Updates, Timesheets and Activities." }, { @@ -83,7 +85,7 @@ def get_data(): "color": "#EF4DB6", "icon": "octicon octicon-broadcast", "type": "module", - "hidden": 1, + "shortcuts": ["Sales Pipeline", "Reports", "Settings"], "description": "Sales pipeline, leads, opportunities and customers." }, { @@ -93,7 +95,7 @@ def get_data(): "color": "#1abc9c", "icon": "fa fa-check-square-o", "type": "module", - "hidden": 1, + "shortcuts": ["Issues", "Warranty", "Reports"], "description": "User interactions, support issues and knowledge base." }, { @@ -103,7 +105,7 @@ def get_data(): "color": "#2ecc71", "icon": "octicon octicon-organization", "type": "module", - "hidden": 1, + "shortcuts": ["Employee and Attendance", "Payroll", "Settings"], "description": "Employees, attendance, payroll, leaves and shifts." }, { @@ -113,7 +115,7 @@ def get_data(): "color": "#1abc9c", "icon": "fa fa-check-square-o", "type": "module", - "hidden": 1, + "shortcuts": ["Goal and Procedure", "Review and Action"], "description": "Quality goals, procedures, reviews and action." }, @@ -126,7 +128,6 @@ def get_data(): "color": "#7f8c8d", "icon": "octicon octicon-tools", "type": "module", - "hidden": 1, "description": "BOMS, work orders, operations, and timesheets." }, { @@ -136,7 +137,6 @@ def get_data(): "color": "#7f8c8d", "icon": "octicon octicon-credit-card", "type": "module", - "hidden": 1, "description": "Point of Sale and cashier closing." }, { @@ -146,7 +146,6 @@ def get_data(): "color": "#428B46", "icon": "octicon octicon-mortar-board", "type": "module", - "hidden": 1, "description": "Student admissions, fees, courses and scores." }, @@ -157,7 +156,6 @@ def get_data(): "color": "#FF888B", "icon": "fa fa-heartbeat", "type": "module", - "hidden": 1, "description": "Patient appointments, procedures and tests." }, { @@ -167,7 +165,6 @@ def get_data(): "color": "#8BC34A", "icon": "octicon octicon-globe", "type": "module", - "hidden": 1, "description": "Crop cycles, land areas, soil and plant analysis." }, { @@ -177,7 +174,6 @@ def get_data(): "color": "#EA81E8", "icon": "fa fa-bed", "type": "module", - "hidden": 1, "description": "Hotel rooms, pricing, reservation and amenities." }, @@ -188,7 +184,6 @@ def get_data(): "color": "#DE2B37", "icon": "octicon octicon-heart", "type": "module", - "hidden": 1, "description": "Volunteers, memberships, grants and chapters." }, { @@ -200,17 +195,16 @@ def get_data(): "_doctype": "Restaurant", "type": "module", "link": "List/Restaurant", - "hidden": 1, "description": "Menu, Orders and Table Reservations." }, - { - "module_name": "Learn", + "module_name": "Help", "category": "Administration", "label": _("Learn"), "color": "#FF888B", "icon": "octicon octicon-device-camera-video", + "type": "module", "is_help": True, "description": "Explore Help Articles and Videos." }, diff --git a/erpnext/config/education.py b/erpnext/config/education.py index a2afa32a84..d5f9e2dca4 100644 --- a/erpnext/config/education.py +++ b/erpnext/config/education.py @@ -8,7 +8,8 @@ def get_data(): "items": [ { "type": "doctype", - "name": "Student" + "name": "Student", + "onboard": 1, }, { "type": "doctype", @@ -181,7 +182,8 @@ def get_data(): "items": [ { "type": "doctype", - "name": "Course" + "name": "Course", + "onboard": 1, }, { "type": "doctype", @@ -189,11 +191,13 @@ def get_data(): }, { "type": "doctype", - "name": "Instructor" + "name": "Instructor", + "onboard": 1, }, { "type": "doctype", - "name": "Room" + "name": "Room", + "onboard": 1, } ] }, @@ -210,7 +214,8 @@ def get_data(): }, { "type": "doctype", - "name": "Grading Scale" + "name": "Grading Scale", + "onboard": 1, }, { "type": "doctype", diff --git a/erpnext/config/healthcare.py b/erpnext/config/healthcare.py index de0167bb98..1311111618 100644 --- a/erpnext/config/healthcare.py +++ b/erpnext/config/healthcare.py @@ -76,11 +76,13 @@ def get_data(): "type": "doctype", "name": "Patient", "label": _("Patient"), + "onboard": 1, }, { "type": "doctype", "name": "Healthcare Practitioner", "label": _("Healthcare Practitioner"), + "onboard": 1, }, { "type": "doctype", @@ -96,6 +98,7 @@ def get_data(): "type": "doctype", "name": "Medical Code", "label": _("Medical Code"), + "onboard": 1, }, { "type": "doctype", @@ -112,6 +115,7 @@ def get_data(): "type": "doctype", "name": "Healthcare Settings", "label": _("Healthcare Settings"), + "onboard": 1, }, { "type": "doctype", diff --git a/erpnext/config/non_profit.py b/erpnext/config/non_profit.py index ba32342d7a..42ec9d3db3 100644 --- a/erpnext/config/non_profit.py +++ b/erpnext/config/non_profit.py @@ -11,6 +11,7 @@ def get_data(): "type": "doctype", "name": "Chapter", "description": _("Chapter information."), + "onboard": 1, } ] }, @@ -21,11 +22,13 @@ def get_data(): "type": "doctype", "name": "Member", "description": _("Member information."), + "onboard": 1, }, { "type": "doctype", "name": "Membership", "description": _("Memebership Details"), + "onboard": 1, }, { "type": "doctype", @@ -41,6 +44,7 @@ def get_data(): "type": "doctype", "name": "Volunteer", "description": _("Volunteer information."), + "onboard": 1, }, { "type": "doctype", diff --git a/erpnext/config/selling.py b/erpnext/config/selling.py index 0750c6ca3b..58f7952ef9 100644 --- a/erpnext/config/selling.py +++ b/erpnext/config/selling.py @@ -119,7 +119,7 @@ def get_data(): "type": "doctype", "name": "Selling Settings", "description": _("Default settings for selling transactions."), - "onboard": 1, + "settings": 1, }, { "type": "doctype", diff --git a/erpnext/config/settings.py b/erpnext/config/settings.py index 179586f183..a97e8ce5d7 100644 --- a/erpnext/config/settings.py +++ b/erpnext/config/settings.py @@ -14,7 +14,7 @@ def get_data(): "label": _("ERPNext Settings"), "description": _("Set Default Values like Company, Currency, Current Fiscal Year, etc."), "hide_count": True, - "onboard": 1, + "settings": 1, } ] }, From 02138b6bfc1fcd02b79d8a9344e68cda1507caa3 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 4 Mar 2019 14:46:28 +0530 Subject: [PATCH 032/129] fix: gross depends upon now report type --- erpnext/accounts/doctype/account/account.json | 4 +-- .../gross_and_net_profit_report.py | 34 ++++++++----------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index 876a3922c9..460c025b69 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -640,7 +640,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval:(((doc.account_type==\"Income Account\") || (doc.account_type==\"Expense Account\")) && (doc.is_group != 1))", + "depends_on": "eval:(doc.report_type == 'Profit and Loss' && !doc.is_group)", "fieldname": "include_in_gross", "fieldtype": "Check", "hidden": 0, @@ -678,7 +678,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-02-08 11:30:46.790603", + "modified": "2019-03-04 14:42:07.208893", "modified_by": "Administrator", "module": "Accounts", "name": "Account", diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py index a0432dbef3..6550981a14 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -26,9 +26,9 @@ def execute(filters=None): columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company) - gross_income = get_revenue(income, period_list, 'gross') + gross_income = get_revenue(income, period_list) - gross_expense = get_revenue(expense, period_list, 'gross') + gross_expense = get_revenue(expense, period_list) if(len(gross_income)==0 and len(gross_expense)== 0): data.append({"account_name": "'" + _("Nothing is included in gross") + "'", @@ -49,11 +49,11 @@ def execute(filters=None): gross_profit = get_profit(gross_income, gross_expense, period_list, filters.company, 'Gross Profit',filters.presentation_currency) data.append(gross_profit) - non_gross_income = get_revenue(income, period_list, 'non_gross') + non_gross_income = get_revenue(income, period_list, 0) data.append({}) data.extend(non_gross_income or []) - non_gross_expense = get_revenue(expense, period_list, 'non_gross') + non_gross_expense = get_revenue(expense, period_list, 0) data.append({}) data.extend(non_gross_expense or []) @@ -63,21 +63,17 @@ def execute(filters=None): return columns, data -def get_revenue(data, period_list, revenue_type): +def get_revenue(data, period_list, include_in_gross=1): + revenue = [item for item in data if item['include_in_gross']==include_in_gross or item['is_group']==1] - if revenue_type == 'gross': - revenue = [item for item in data if item['include_in_gross']==1 or item['is_group']==1] - elif revenue_type == 'non_gross': - revenue = [item for item in data if item['include_in_gross']==0 or item['is_group']==1] - - revenue, status = remove_parent_with_no_child(revenue, period_list) - while status == "data to be removed": - revenue, status = remove_parent_with_no_child(revenue, period_list) + data_to_be_removed =True + while data_to_be_removed: + revenue, data_to_be_removed = remove_parent_with_no_child(revenue, period_list) revenue = adjust_account(revenue, period_list) return copy.deepcopy(revenue) def remove_parent_with_no_child(data, period_list): - status = "nothing to remove" + data_to_be_removed = False for parent in data: if 'is_group' in parent and parent.get("is_group") == 1: have_child = False @@ -87,10 +83,10 @@ def remove_parent_with_no_child(data, period_list): break if not have_child: - status = "data to be removed" + data_to_be_removed = True data.remove(parent) - return data, status + return data, data_to_be_removed def adjust_account(data, period_list, consolidated= False): leaf_nodes = [item for item in data if item['is_group'] == 0] @@ -127,7 +123,7 @@ def get_profit(gross_income, gross_expense, period_list, company, profit_type, c for period in period_list: key = period if consolidated else period.key - profit_loss[key] = flt(gross_income[0][key] if len(gross_income) else 0) - flt(gross_expense[0][key] if len(gross_expense) else 0) + profit_loss[key] = flt(gross_income[0].get(key, 0)) - flt(gross_expense[0].get(key, 0)) if profit_loss[key]: has_value=True @@ -147,8 +143,8 @@ def get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expe for period in period_list: key = period if consolidated else period.key - total_income = flt(gross_income[0][key] if len(gross_income) else 0) + flt(non_gross_income[0][key] if len(non_gross_income) else 0) - total_expense = flt(gross_expense[0][key] if len(gross_expense) else 0) + flt(non_gross_expense[0][key] if len(non_gross_expense) else 0) + total_income = flt(gross_income[0].get(key, 0)) + flt(non_gross_income[0].get(key, 0)) + total_expense = flt(gross_expense[0].get(key, 0)) + flt(non_gross_expense[0].get(key, 0)) profit_loss[key] = flt(total_income) - flt(total_expense) if profit_loss[key]: From 0c9945e0bf97bf8802bb458dc183c0797f048325 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 4 Mar 2019 16:14:53 +0530 Subject: [PATCH 033/129] Minor fixes --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 2 +- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 +- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 2 +- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- .../bank_and_cash_payment_voucher.html | 4 ++-- .../journal_auditing_voucher/journal_auditing_voucher.html | 4 ++-- .../purchase_auditing_voucher/purchase_auditing_voucher.html | 2 +- .../sales_auditing_voucher/sales_auditing_voucher.html | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 27c946ddc1..7c48b5c4f8 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -53,7 +53,7 @@ class JournalEntry(AccountsController): self.update_inter_company_jv() def before_print(self): - self.gl = frappe.get_list("GL Entry",filters={"voucher_type": "Journal Entry", + self.gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Journal Entry", "voucher_no": self.name} , fields=["account", "party_type", "party", "debit", "credit", "remarks"] ) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 00ffd17234..2f56a51a73 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -71,7 +71,7 @@ class PaymentEntry(AccountsController): self.update_expense_claim() def before_print(self): - self.gl = frappe.get_list("GL Entry",filters={"voucher_type": "Payment Entry", + self.gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Payment Entry", "voucher_no": self.name} , fields=["account", "party_type", "party", "debit", "credit", "remarks"] ) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index cbade186ad..c3978d78db 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -54,7 +54,7 @@ class PurchaseInvoice(BuyingController): self.release_date = '' def before_print(self): - self.gl = frappe.get_list("GL Entry",filters={"voucher_type": "Purchase Invoice", + self.gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Purchase Invoice", "voucher_no": self.name} , fields=["account", "party_type", "party", "debit", "credit"] ) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 5bc127239a..6eea8ad764 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -206,7 +206,7 @@ class SalesInvoice(SellingController): self.update_time_sheet(None) def before_print(self): - self.gl = frappe.get_list("GL Entry",filters={"voucher_type": "Sales Invoice", + self.gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Sales Invoice", "voucher_no": self.name} , fields=["account", "party_type", "party", "debit", "credit"] ) diff --git a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html index 7b1a8a2a1c..2eadb2a92a 100644 --- a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html +++ b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html @@ -34,7 +34,7 @@ Credit {% set total_credit = 0 -%} - {% for entries in doc.gl %} + {% for entries in doc.gl_entries %} {% if entries.debit == 0.0 %} {{ entries.account }} @@ -59,7 +59,7 @@ Debit {% set total_debit = 0 -%} - {% for entries in doc.gl %} + {% for entries in doc.gl_entries %} {% if entries.credit == 0.0 %} {{ entries.account }} diff --git a/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html index cacb5f2a57..4565559084 100644 --- a/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html +++ b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html @@ -34,7 +34,7 @@ Credit {% set total_credit = 0 -%} - {% for entries in doc.gl %} + {% for entries in doc.gl_entries %} {% if entries.debit == 0.0 %} {{ entries.account }} @@ -56,7 +56,7 @@ Debit {% set total_debit = 0 -%} - {% for entries in doc.gl %} + {% for entries in doc.gl_entries %} {% if entries.credit == 0.0 %} {{ entries.account }} diff --git a/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html index c8bd5c21ec..35852e1e1c 100644 --- a/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html +++ b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html @@ -79,7 +79,7 @@ Credit Amount Debit Amount - {% for entries in doc.gl %} + {% for entries in doc.gl_entries %} {{ loop.index }} {{ entries.account }} diff --git a/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html index b3ce888fa5..04de83de70 100644 --- a/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html +++ b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html @@ -73,7 +73,7 @@ Credit Amount Debit Amount - {% for entries in doc.gl %} + {% for entries in doc.gl_entries %} {{ loop.index }} {{ entries.account }} From acb7238ad69a178dbf26d84af89f343590e6d432 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Tue, 5 Mar 2019 12:20:19 +0530 Subject: [PATCH 034/129] Allow zero amount in additional salary --- erpnext/hr/doctype/additional_salary/additional_salary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py index 6f87954f50..e25e69e75f 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.py +++ b/erpnext/hr/doctype/additional_salary/additional_salary.py @@ -11,8 +11,8 @@ from frappe.utils import getdate, date_diff class AdditionalSalary(Document): def validate(self): self.validate_dates() - if self.amount <= 0: - frappe.throw(_("Amount should be greater than zero.")) + if self.amount < 0: + frappe.throw(_("Amount should not be less than zero.")) def validate_dates(self): date_of_joining, relieving_date = frappe.db.get_value("Employee", self.employee, From e062acbbd35dc851e897e9b350fb1627d5363c60 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Tue, 5 Mar 2019 17:17:10 +0530 Subject: [PATCH 035/129] fix: Removed shortcuts from desktop.py --- erpnext/config/desktop.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/erpnext/config/desktop.py b/erpnext/config/desktop.py index 48418f1d16..6dc95b242c 100644 --- a/erpnext/config/desktop.py +++ b/erpnext/config/desktop.py @@ -14,7 +14,6 @@ def get_data(): "icon": "fa fa-check-square-o", "type": "module", "disable_after_onboard": 1, - "shortcuts": ["Import Data", "Letter Head", "Email Account", "Item", "Customer"], "description": "Dive into the basics for your organisation's needs.", "onboard_present": 1 }, @@ -25,7 +24,6 @@ def get_data(): "color": "#3498db", "icon": "octicon octicon-repo", "type": "module", - "shortcuts": ["Item", "Customer", "Supplier", "General Ledger", "Sales Invoice"], "description": "Accounts, billing, payments, cost center and budgeting." }, { @@ -35,7 +33,6 @@ def get_data(): "color": "#1abc9c", "icon": "octicon octicon-tag", "type": "module", - "shortcuts": ["Quotation", "Sales Order", "Sales Analytics", "Customer"], "description": "Sales orders, quotations, customers and items." }, { @@ -45,7 +42,6 @@ def get_data(): "color": "#c0392b", "icon": "octicon octicon-briefcase", "type": "module", - "shortcuts": ["Purchase Order", "Items and Pricing", "Settings", "Supplier"], "description": "Purchasing, suppliers, material requests, and items." }, { @@ -55,7 +51,6 @@ def get_data(): "color": "#f39c12", "icon": "octicon octicon-package", "type": "module", - "shortcuts": ["Transactions", "Stock Reports", "Settings"], "description": "Stock transactions, reports, serial numbers and batches." }, { @@ -65,7 +60,6 @@ def get_data(): "color": "#4286f4", "icon": "octicon octicon-database", "type": "module", - "shortcuts": ["Assets", "Maintanence", "Reports"], "description": "Asset movement, maintainance and tools." }, { @@ -75,7 +69,6 @@ def get_data(): "color": "#8e44ad", "icon": "octicon octicon-rocket", "type": "module", - "shortcuts": ["Projects", "Time Tracking", "Reports"], "description": "Updates, Timesheets and Activities." }, { @@ -85,7 +78,6 @@ def get_data(): "color": "#EF4DB6", "icon": "octicon octicon-broadcast", "type": "module", - "shortcuts": ["Sales Pipeline", "Reports", "Settings"], "description": "Sales pipeline, leads, opportunities and customers." }, { @@ -95,7 +87,6 @@ def get_data(): "color": "#1abc9c", "icon": "fa fa-check-square-o", "type": "module", - "shortcuts": ["Issues", "Warranty", "Reports"], "description": "User interactions, support issues and knowledge base." }, { @@ -105,7 +96,6 @@ def get_data(): "color": "#2ecc71", "icon": "octicon octicon-organization", "type": "module", - "shortcuts": ["Employee and Attendance", "Payroll", "Settings"], "description": "Employees, attendance, payroll, leaves and shifts." }, { @@ -115,7 +105,6 @@ def get_data(): "color": "#1abc9c", "icon": "fa fa-check-square-o", "type": "module", - "shortcuts": ["Goal and Procedure", "Review and Action"], "description": "Quality goals, procedures, reviews and action." }, From 219b9f4150641f2119ea9ab89851dacf1a933098 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Tue, 5 Mar 2019 17:44:07 +0530 Subject: [PATCH 036/129] Salary Component can be equal to zero --- erpnext/hr/doctype/additional_salary/additional_salary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py index 6f87954f50..e25e69e75f 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.py +++ b/erpnext/hr/doctype/additional_salary/additional_salary.py @@ -11,8 +11,8 @@ from frappe.utils import getdate, date_diff class AdditionalSalary(Document): def validate(self): self.validate_dates() - if self.amount <= 0: - frappe.throw(_("Amount should be greater than zero.")) + if self.amount < 0: + frappe.throw(_("Amount should not be less than zero.")) def validate_dates(self): date_of_joining, relieving_date = frappe.db.get_value("Employee", self.employee, From 33d0a9f6841cc1c239d79753ba86d5daebada14b Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Tue, 5 Mar 2019 21:57:11 +0100 Subject: [PATCH 037/129] fix(projects): change fieldtype of billing amounts to Currency --- erpnext/projects/doctype/timesheet/timesheet.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json index 5ad2ab3a37..c29c11b746 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.json +++ b/erpnext/projects/doctype/timesheet/timesheet.json @@ -739,7 +739,7 @@ "collapsible": 0, "columns": 0, "fieldname": "total_costing_amount", - "fieldtype": "Float", + "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -805,7 +805,7 @@ "depends_on": "", "description": "", "fieldname": "total_billable_amount", - "fieldtype": "Float", + "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -837,7 +837,7 @@ "collapsible": 0, "columns": 0, "fieldname": "total_billed_amount", - "fieldtype": "Float", + "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -1000,7 +1000,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-01-30 11:28:18.518590", + "modified": "2019-03-05 21:54:02.654690", "modified_by": "Administrator", "module": "Projects", "name": "Timesheet", From b263876918bd05a75acf7b73d154161abecc9453 Mon Sep 17 00:00:00 2001 From: bcornwellmott Date: Tue, 5 Mar 2019 16:44:02 -0800 Subject: [PATCH 038/129] Add date to currency exchange caching The current currency exchange caching does not keep track of the transaction date, so if you are backdating transactions, the system probably isn't pulling the correct exchange rate (if it's looking up the currency exchange rate on the fly) --- erpnext/setup/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 01e0b7d441..d1c206d8b1 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -93,7 +93,7 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No try: cache = frappe.cache() - key = "currency_exchange_rate:{0}:{1}".format(from_currency, to_currency) + key = "currency_exchange_rate_{0}:{1}:{2}".format(transaction_date,from_currency, to_currency) value = cache.get(key) if not value: @@ -143,4 +143,4 @@ def insert_record(records): def welcome_email(): site_name = get_default_company() title = _("Welcome to {0}".format(site_name)) - return title \ No newline at end of file + return title From 3d0121369f5321cd4f91e236a4ffd48ec02e7744 Mon Sep 17 00:00:00 2001 From: Joyce Babu Date: Wed, 6 Mar 2019 13:04:45 +0530 Subject: [PATCH 039/129] Add 'Half-Yearly' option to Earned Leave Frequency in addition to the current values of Monthly, Quarterly and Yearly --- erpnext/hr/doctype/leave_type/leave_type.json | 2 +- erpnext/hr/utils.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index 8f7b5a876c..6a7a80a784 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -561,7 +561,7 @@ "label": "Earned Leave Frequency", "length": 0, "no_copy": 0, - "options": "Monthly\nQuarterly\nYearly", + "options": "Monthly\nQuarterly\nHalf-Yearly\nYearly", "permlevel": 0, "precision": "", "print_hide": 0, diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 02262012f1..e0b6a51d1a 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -260,7 +260,7 @@ def allocate_earned_leaves(): fields=["name", "max_leaves_allowed", "earned_leave_frequency", "rounding"], filters={'is_earned_leave' : 1}) today = getdate() - divide_by_frequency = {"Yearly": 1, "Quarterly": 4, "Monthly": 12} + divide_by_frequency = {"Yearly": 1, "Half-Yearly": 6, "Quarterly": 4, "Monthly": 12} if e_leave_types: for e_leave_type in e_leave_types: leave_allocations = frappe.db.sql("""select name, employee, from_date, to_date from `tabLeave Allocation` where '{0}' @@ -297,6 +297,9 @@ def check_frequency_hit(from_date, to_date, frequency): if frequency == "Quarterly": if not months % 3: return True + elif frequency == "Half-Yearly": + if not months % 6: + return True elif frequency == "Yearly": if not months % 12: return True From 29c46bb311dbacd71a7f7875ceff47eda8060c12 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 6 Mar 2019 14:42:50 +0530 Subject: [PATCH 040/129] fix(sales_order.py): handle zero bundle qty --- erpnext/manufacturing/doctype/work_order/work_order.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 9873efa124..947d6931e2 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -282,6 +282,10 @@ class WorkOrder(Document): total_bundle_qty = frappe.db.sql(""" select sum(qty) from `tabProduct Bundle Item` where parent = %s""", (frappe.db.escape(self.product_bundle_item)))[0][0] + if not total_bundle_qty: + # product bundle is 0 (product bundle allows 0 qty for items) + total_bundle_qty = 1 + cond = "product_bundle_item = %s" if self.product_bundle_item else "production_item = %s" qty = frappe.db.sql(""" select sum(qty) from From e66f59654e67649611f309d172087b05ac7ea023 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Wed, 6 Mar 2019 15:42:32 +0530 Subject: [PATCH 041/129] fix(report): Incorrectdata for balance quantity and value in Stock Balance report --- erpnext/stock/report/stock_balance/stock_balance.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index d6b171f93c..ae919b6019 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -43,11 +43,11 @@ def execute(filters=None): item_map[item]["item_group"], item_map[item]["brand"], item_map[item]["description"], warehouse, - item_map[item]["stock_uom"], qty_dict.opening_qty, + item_map[item]["stock_uom"], qty_dict.bal_qty, + qty_dict.bal_val, qty_dict.opening_qty, qty_dict.opening_val, qty_dict.in_qty, qty_dict.in_val, qty_dict.out_qty, - qty_dict.out_val, qty_dict.bal_qty, - qty_dict.bal_val, qty_dict.val_rate, + qty_dict.out_val, qty_dict.val_rate, item_reorder_level, item_reorder_qty, company From 9757a603fa99c3d05151c7b94d2eddffb04a6ddb Mon Sep 17 00:00:00 2001 From: Jay Parikh Date: Wed, 6 Mar 2019 12:44:41 +0000 Subject: [PATCH 042/129] Fix Company Default to Total Stock Summary Report --- .../stock/report/total_stock_summary/total_stock_summary.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.js b/erpnext/stock/report/total_stock_summary/total_stock_summary.js index 223a603004..b7461c485f 100644 --- a/erpnext/stock/report/total_stock_summary/total_stock_summary.js +++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.js @@ -18,7 +18,9 @@ frappe.query_reports["Total Stock Summary"] = { "label": __("Company"), "fieldtype": "Link", "width": "80", - "options": "Company" + "options": "Company", + "default": frappe.defaults.get_user_default("Company"), + "reqd": 1 }, ] } From db8500c03af758f7640e78580efcaa66bc2fedd4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 6 Mar 2019 18:18:21 +0530 Subject: [PATCH 043/129] POS profile, account for change amount must be cash or bank account --- erpnext/accounts/doctype/pos_profile/pos_profile.js | 8 ++++++++ .../accounts/doctype/sales_invoice/sales_invoice.js | 8 ++++++++ .../sales_invoice_payment/sales_invoice_payment.json | 12 ++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js index 13d53d1f6a..a6386ddc4b 100755 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.js +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js @@ -33,6 +33,14 @@ frappe.ui.form.on('POS Profile', { }; }); + frm.set_query("account_for_change_amount", function() { + return { + filters: { + account_type: ['in', ["Cash", "Bank"]] + } + }; + }); + frm.set_query("print_format", function() { return { filters: { doc_type: "Sales Invoice", print_format_type: "Js"} }; }); diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index b1a851a4f1..3816632065 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -556,6 +556,14 @@ frappe.ui.form.on('Sales Invoice', { frm.add_fetch('payment_term', 'invoice_portion', 'invoice_portion'); frm.add_fetch('payment_term', 'description', 'description'); + frm.set_query("account_for_change_amount", function() { + return { + filters: { + account_type: ['in', ["Cash", "Bank"]] + } + }; + }); + frm.custom_make_buttons = { 'Delivery Note': 'Delivery', 'Sales Invoice': 'Sales Return', diff --git a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json index ccdabfe544..1c5962acf6 100644 --- a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json +++ b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json @@ -20,6 +20,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:parent.doctype == 'POS Profile'", + "fetch_if_empty": 0, "fieldname": "default", "fieldtype": "Check", "hidden": 0, @@ -52,6 +53,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "mode_of_payment", "fieldtype": "Link", "hidden": 0, @@ -85,8 +87,9 @@ "bold": 0, "collapsible": 0, "columns": 0, - "default": "", + "default": "0", "depends_on": "eval:parent.doctype == 'Sales Invoice'", + "fetch_if_empty": 0, "fieldname": "amount", "fieldtype": "Currency", "hidden": 0, @@ -120,6 +123,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_3", "fieldtype": "Column Break", "hidden": 0, @@ -151,6 +155,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "account", "fieldtype": "Link", "hidden": 0, @@ -185,6 +190,7 @@ "collapsible": 0, "columns": 0, "fetch_from": "mode_of_payment.type", + "fetch_if_empty": 0, "fieldname": "type", "fieldtype": "Read Only", "hidden": 0, @@ -218,6 +224,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_amount", "fieldtype": "Currency", "hidden": 0, @@ -251,6 +258,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "clearance_date", "fieldtype": "Date", "hidden": 0, @@ -287,7 +295,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2019-02-18 15:03:59.720469", + "modified": "2019-03-06 15:58:37.839241", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Payment", From 783331c64573cf56635bbe1cdb8e59c8f32db018 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Wed, 6 Mar 2019 18:44:04 +0530 Subject: [PATCH 044/129] sales_order to work_order item desc fix --- erpnext/selling/doctype/sales_order/sales_order.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index b589cdeaa4..2345762233 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -388,6 +388,7 @@ class SalesOrder(SellingController): items.append(dict( name= i.name, item_code= i.item_code, + description= i.description, bom = bom, warehouse = i.warehouse, pending_qty = pending_qty, @@ -398,6 +399,7 @@ class SalesOrder(SellingController): items.append(dict( name= i.name, item_code= i.item_code, + description= i.description, bom = '', warehouse = i.warehouse, pending_qty = pending_qty, @@ -901,7 +903,8 @@ def make_work_orders(items, sales_order, company, project=None): sales_order=sales_order, sales_order_item=i['sales_order_item'], project=project, - fg_warehouse=i['warehouse'] + fg_warehouse=i['warehouse'], + description=i['description'] )).insert() work_order.set_work_order_operations() work_order.save() From 4f1737c2dd248850f8d086736e170b44b66d1a2f Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Wed, 6 Mar 2019 22:19:15 +0530 Subject: [PATCH 045/129] fix sales order test for creating work order from sales order --- erpnext/selling/doctype/sales_order/test_sales_order.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 0eb19e3417..f270938ad3 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -573,7 +573,8 @@ class TestSalesOrder(unittest.TestCase): "item_code": item.get("item_code"), "pending_qty": item.get("pending_qty"), "sales_order_item": item.get("sales_order_item"), - "bom": item.get("bom") + "bom": item.get("bom"), + "description": item.get("description") }) so_item_name[item.get("sales_order_item")]= item.get("pending_qty") make_work_orders(json.dumps({"items":po_items}), so.name, so.company) From 59699bf46e2288ef89208ed7a602016455754aef Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Wed, 6 Mar 2019 23:33:03 +0530 Subject: [PATCH 046/129] [debug] locally tests passed but travis failed --- erpnext/selling/doctype/sales_order/sales_order.py | 1 + erpnext/selling/doctype/sales_order/test_sales_order.py | 1 + 2 files changed, 2 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 2345762233..a84fa1430b 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -886,6 +886,7 @@ def get_supplier(doctype, txt, searchfield, start, page_len, filters): def make_work_orders(items, sales_order, company, project=None): '''Make Work Orders against the given Sales Order for the given `items`''' items = json.loads(items).get('items') + print(items) out = [] for i in items: diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index f270938ad3..584b5b99c6 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -577,6 +577,7 @@ class TestSalesOrder(unittest.TestCase): "description": item.get("description") }) so_item_name[item.get("sales_order_item")]= item.get("pending_qty") + print(po_items) make_work_orders(json.dumps({"items":po_items}), so.name, so.company) # Check if Work Orders were raised From 7ab961f79881c4861dd2c7664aaceb165ebc3bc7 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Wed, 6 Mar 2019 23:53:38 +0530 Subject: [PATCH 047/129] remove debug code --- erpnext/selling/doctype/sales_order/sales_order.py | 1 - erpnext/selling/doctype/sales_order/test_sales_order.py | 1 - 2 files changed, 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index a84fa1430b..2345762233 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -886,7 +886,6 @@ def get_supplier(doctype, txt, searchfield, start, page_len, filters): def make_work_orders(items, sales_order, company, project=None): '''Make Work Orders against the given Sales Order for the given `items`''' items = json.loads(items).get('items') - print(items) out = [] for i in items: diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 584b5b99c6..f270938ad3 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -577,7 +577,6 @@ class TestSalesOrder(unittest.TestCase): "description": item.get("description") }) so_item_name[item.get("sales_order_item")]= item.get("pending_qty") - print(po_items) make_work_orders(json.dumps({"items":po_items}), so.name, so.company) # Check if Work Orders were raised From e9db0d2c61a11c7b6c5ce7757d6df732a69e6398 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 7 Mar 2019 12:50:02 +0530 Subject: [PATCH 048/129] fix(minor): update task list label from Closed to Completed --- erpnext/projects/doctype/task/task_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/task/task_list.js b/erpnext/projects/doctype/task/task_list.js index 29d2a738f4..941fe97546 100644 --- a/erpnext/projects/doctype/task/task_list.js +++ b/erpnext/projects/doctype/task/task_list.js @@ -9,7 +9,7 @@ frappe.listview_settings['Task'] = { listview.call_for_selected_items(method, {"status": "Open"}); }); - listview.page.add_menu_item(__("Set as Closed"), function() { + listview.page.add_menu_item(__("Set as Completed"), function() { listview.call_for_selected_items(method, {"status": "Completed"}); }); }, From 703a5974784659ea5c8fedf08bb735b21443f524 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 7 Mar 2019 13:03:40 +0530 Subject: [PATCH 049/129] fix(patch): update customization options if completed is not present --- erpnext/patches/v12_0/set_task_status.py | 12 ++++++++++++ erpnext/projects/doctype/project/project.js | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v12_0/set_task_status.py b/erpnext/patches/v12_0/set_task_status.py index 44000cf6fc..32b8177130 100644 --- a/erpnext/patches/v12_0/set_task_status.py +++ b/erpnext/patches/v12_0/set_task_status.py @@ -1,5 +1,17 @@ import frappe def execute(): + frappe.reload_doctype('Task') + frappe.reload_doctype('Project Task') + + # add "Completed" if customized + for doctype in ('Task', 'Project Task'): + property_setter_name = frappe.db.exists('Property Setter', dict(doc_type = doctype, field_name = 'status', property = 'options')) + if property_setter_name: + property_setter = frappe.get_doc('Property Setter', property_setter_name) + if not "Completed" in property_setter.value: + property_setter.value = property_setter.value + '\nCompleted' + property_setter.save() + # renamed default status to Completed as status "Closed" is ambiguous frappe.db.sql('update tabTask set status = "Completed" where status = "Closed"') \ No newline at end of file diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index a366a25bb4..6de6454a0e 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -9,7 +9,7 @@ frappe.ui.form.on("Project", { indicator = 'red'; } else if (doc.status == 'Cancelled') { indicator = 'dark grey'; - } else if (doc.status == 'Closed') { + } else if (doc.status == 'Completed') { indicator = 'green'; } return indicator; From 1b0f5fd4304e8d2cd9c24de659c3cf8678b893aa Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Thu, 7 Mar 2019 16:17:00 +0530 Subject: [PATCH 050/129] fix: pass correct argument to method get_deprication_amount --- erpnext/assets/doctype/asset/asset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index a38b40bc60..3cab3b348f 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -255,7 +255,7 @@ class Asset(AccountsController): def get_depreciation_amount(self, depreciable_value, total_number_of_depreciations, row): percentage_value = 100.0 if row.depreciation_method == 'Written Down Value' else 200.0 - factor = percentage_value / total_number_of_depreciations + factor = percentage_value / cint(total_number_of_depreciations) depreciation_amount = flt(depreciable_value * factor / 100, 0) value_after_depreciation = flt(depreciable_value) - depreciation_amount @@ -275,7 +275,7 @@ class Asset(AccountsController): flt(row.expected_value_after_useful_life)) / (cint(row.total_number_of_depreciations) - cint(self.number_of_depreciations_booked)) * prorata_temporis else: - depreciation_amount = self.get_depreciation_amount(depreciable_value, row) + depreciation_amount = self.get_depreciation_amount(depreciable_value, row.total_number_of_depreciations, row) return depreciation_amount From 78a32ae172063a9456c51185ccb348d97db1c58a Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 7 Mar 2019 20:00:07 +0530 Subject: [PATCH 051/129] fix: Remove validation for relieving date from additional salary --- erpnext/hr/doctype/additional_salary/additional_salary.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py index e25e69e75f..968a1c4571 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.py +++ b/erpnext/hr/doctype/additional_salary/additional_salary.py @@ -19,8 +19,6 @@ class AdditionalSalary(Document): ["date_of_joining", "relieving_date"]) if date_of_joining and getdate(self.payroll_date) < getdate(date_of_joining): frappe.throw(_("Payroll date can not be less than employee's joining date")) - elif relieving_date and getdate(self.payroll_date) > getdate(relieving_date): - frappe.throw(_("To date can not greater than employee's relieving date")) def get_amount(self, sal_start_date, sal_end_date): start_date = getdate(sal_start_date) From 9ec4816b3248abdad16a95345629d2d2bae1a08a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 8 Mar 2019 10:44:13 +0530 Subject: [PATCH 052/129] Update item_barcode_childtable_migrate.py --- erpnext/patches/v10_0/item_barcode_childtable_migrate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v10_0/item_barcode_childtable_migrate.py b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py index bc6005677d..e30e0a74c0 100644 --- a/erpnext/patches/v10_0/item_barcode_childtable_migrate.py +++ b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py @@ -27,5 +27,5 @@ def execute(): 'parent': item.name, 'parentfield': 'barcodes' }).insert() - except frappe.DuplicateEntryError: + except (frappe.DuplicateEntryError, frappe.UniqueValidationError): continue From 821a002125cfed5b71559b7281c3f8cd7c634bae Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 7 Mar 2019 19:27:54 +0530 Subject: [PATCH 053/129] feat: added provision to disable CWIP accounting in asset settings --- .../purchase_invoice/purchase_invoice.py | 18 +++++-- erpnext/assets/doctype/asset/asset.py | 23 ++++++--- .../asset_settings/asset_settings.json | 50 +++++++++++++++++-- .../purchase_receipt/purchase_receipt.py | 5 +- 4 files changed, 78 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index c0d0d837fe..bc44bc80f4 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -8,6 +8,7 @@ from frappe.utils import cint, cstr, formatdate, flt, getdate, nowdate from frappe import _, throw import frappe.defaults +from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.controllers.buying_controller import BuyingController from erpnext.accounts.party import get_party_account, get_due_date from erpnext.accounts.utils import get_account_currency, get_fiscal_year @@ -17,7 +18,7 @@ from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entri from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt from erpnext.buying.utils import check_for_closed_status from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center -from erpnext.assets.doctype.asset.asset import get_asset_account +from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_disabled from frappe.model.mapper import get_mapped_doc from six import iteritems from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_invoice,\ @@ -238,6 +239,13 @@ class PurchaseInvoice(BuyingController): item.expense_account = warehouse_account[item.warehouse]["account"] else: item.expense_account = stock_not_billed_account + elif item.is_fixed_asset and is_cwip_accounting_disabled(): + if not item.asset: + frappe.throw(_("Row {0}: asset is required for item {1}") + .format(item.idx, item.item_code)) + + item.expense_account = get_asset_category_account(item.asset, 'fixed_asset_account', + company = self.company) elif item.is_fixed_asset and item.pr_detail: item.expense_account = asset_received_but_not_billed elif not item.expense_account and for_validate: @@ -383,7 +391,9 @@ class PurchaseInvoice(BuyingController): self.make_supplier_gl_entry(gl_entries) self.make_item_gl_entries(gl_entries) - self.get_asset_gl_entry(gl_entries) + if not is_cwip_accounting_disabled(): + self.get_asset_gl_entry(gl_entries) + self.make_tax_gl_entries(gl_entries) gl_entries = merge_similar_entries(gl_entries) @@ -475,7 +485,7 @@ class PurchaseInvoice(BuyingController): "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "credit": flt(item.rm_supp_cost) }, warehouse_account[self.supplier_warehouse]["account_currency"])) - elif not item.is_fixed_asset: + elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()): gl_entries.append( self.get_gl_dict({ "account": item.expense_account if not item.enable_deferred_expense else item.deferred_expense_account, @@ -520,7 +530,7 @@ class PurchaseInvoice(BuyingController): base_asset_amount = flt(item.base_net_amount + item.item_tax_amount) if (not item.expense_account or frappe.db.get_value('Account', - item.expense_account, 'account_type') != 'Asset Received But Not Billed'): + item.expense_account, 'account_type') not in ['Asset Received But Not Billed', 'Fixed Asset']): arbnb_account = self.get_company_default("asset_received_but_not_billed") item.expense_account = arbnb_account diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index a38b40bc60..6b145956f3 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -33,7 +33,7 @@ class Asset(AccountsController): self.validate_in_use_date() self.set_status() self.update_stock_movement() - if not self.booked_fixed_asset: + if not self.booked_fixed_asset and not is_cwip_accounting_disabled(): self.make_gl_entries() def on_cancel(self): @@ -71,14 +71,15 @@ class Asset(AccountsController): if not flt(self.gross_purchase_amount): frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError) - if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice): - frappe.throw(_("Please create purchase receipt or purchase invoice for the item {0}"). - format(self.item_code)) + if not is_cwip_accounting_disabled(): + if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice): + frappe.throw(_("Please create purchase receipt or purchase invoice for the item {0}"). + format(self.item_code)) - if (not self.purchase_receipt and self.purchase_invoice - and not frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')): - frappe.throw(_("Update stock must be enable for the purchase invoice {0}"). - format(self.purchase_invoice)) + if (not self.purchase_receipt and self.purchase_invoice + and not frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')): + frappe.throw(_("Update stock must be enable for the purchase invoice {0}"). + format(self.purchase_invoice)) if not self.calculate_depreciation: return @@ -404,6 +405,9 @@ def update_maintenance_status(): asset.set_status('Out of Order') def make_post_gl_entry(): + if is_cwip_accounting_disabled(): + return + assets = frappe.db.sql_list(""" select name from `tabAsset` where ifnull(booked_fixed_asset, 0) = 0 and available_for_use_date = %s""", nowdate()) @@ -551,3 +555,6 @@ def make_journal_entry(asset_name): }) return je + +def is_cwip_accounting_disabled(): + return cint(frappe.db.get_single_value("Asset Settings", "disable_cwip_accounting")) \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_settings/asset_settings.json b/erpnext/assets/doctype/asset_settings/asset_settings.json index d6ddd33c30..a3fee96f4e 100644 --- a/erpnext/assets/doctype/asset_settings/asset_settings.json +++ b/erpnext/assets/doctype/asset_settings/asset_settings.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, @@ -14,10 +15,12 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "depreciation_options", "fieldtype": "Section Break", "hidden": 0, @@ -40,14 +43,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "schedule_based_on_fiscal_year", "fieldtype": "Check", "hidden": 0, @@ -70,10 +76,12 @@ "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, @@ -81,6 +89,7 @@ "default": "360", "depends_on": "eval:doc.schedule_based_on_fiscal_year", "description": "This value is used for pro-rata temporis calculation", + "fetch_if_empty": 0, "fieldname": "number_of_days_in_fiscal_year", "fieldtype": "Data", "hidden": 0, @@ -103,6 +112,40 @@ "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, + "fetch_if_empty": 0, + "fieldname": "disable_cwip_accounting", + "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": "Disable CWIP Accounting", + "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 } ], @@ -116,7 +159,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-01-05 10:10:39.803255", + "modified": "2019-03-08 10:44:41.924547", "modified_by": "Administrator", "module": "Assets", "name": "Asset Settings", @@ -125,7 +168,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -145,7 +187,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -171,5 +212,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index cb51e220b8..ed7f2ca432 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -13,7 +13,7 @@ from erpnext.controllers.buying_controller import BuyingController from erpnext.accounts.utils import get_account_currency from frappe.desk.notifications import clear_doctype_notifications from erpnext.buying.utils import check_for_closed_status -from erpnext.assets.doctype.asset.asset import get_asset_account +from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_disabled from six import iteritems form_grid_templates = { @@ -258,7 +258,8 @@ class PurchaseReceipt(BuyingController): d.rejected_warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(d.warehouse) - self.get_asset_gl_entry(gl_entries) + if not is_cwip_accounting_disabled(): + self.get_asset_gl_entry(gl_entries) # Cost center-wise amount breakup for other charges included for valuation valuation_tax = {} for tax in self.get("taxes"): From 6b33c9b9342ed39cc059cf89432345b58b400976 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 8 Mar 2019 11:13:35 +0530 Subject: [PATCH 054/129] fix: purchase receipt not able to submit because default inventory account has not selected in another company --- .../doctype/purchase_invoice/purchase_invoice.py | 7 ++++--- .../accounts/doctype/sales_invoice/sales_invoice.py | 3 ++- erpnext/accounts/utils.py | 4 ++-- erpnext/controllers/stock_controller.py | 13 +++++++------ .../v7_0/repost_future_gle_for_purchase_invoice.py | 11 ++++++----- erpnext/stock/__init__.py | 11 ++++++++--- 6 files changed, 29 insertions(+), 20 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index c0d0d837fe..df1f29aa22 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -222,7 +222,7 @@ class PurchaseInvoice(BuyingController): self.validate_item_code() self.validate_warehouse() if auto_accounting_for_stock: - warehouse_account = get_warehouse_account_map() + warehouse_account = get_warehouse_account_map(self.company) for item in self.get("items"): # in case of auto inventory accounting, @@ -366,7 +366,8 @@ class PurchaseInvoice(BuyingController): if repost_future_gle and cint(self.update_stock) and self.auto_accounting_for_stock: from erpnext.controllers.stock_controller import update_gl_entries_after items, warehouses = self.get_items_and_warehouses() - update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items) + update_gl_entries_after(self.posting_date, self.posting_time, + warehouses, items, company = self.company) elif self.docstatus == 2 and cint(self.update_stock) and self.auto_accounting_for_stock: delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name) @@ -423,7 +424,7 @@ class PurchaseInvoice(BuyingController): stock_items = self.get_stock_items() expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") if self.update_stock and self.auto_accounting_for_stock: - warehouse_account = get_warehouse_account_map() + warehouse_account = get_warehouse_account_map(self.company) voucher_wise_stock_value = {} if self.update_stock: diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 62d8ffd897..926e2b8c52 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -695,7 +695,8 @@ class SalesInvoice(SellingController): if repost_future_gle and cint(self.update_stock) \ and cint(auto_accounting_for_stock): items, warehouses = self.get_items_and_warehouses() - update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items) + update_gl_entries_after(self.posting_date, self.posting_time, + warehouses, items, company = self.company) elif self.docstatus == 2 and cint(self.update_stock) \ and cint(auto_accounting_for_stock): from erpnext.accounts.general_ledger import delete_gl_entries diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index d4e1840eb9..2de5104183 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -544,14 +544,14 @@ def fix_total_debit_credit(): (dr_or_cr, dr_or_cr, '%s', '%s', '%s', dr_or_cr), (d.diff, d.voucher_type, d.voucher_no)) -def get_stock_and_account_difference(account_list=None, posting_date=None): +def get_stock_and_account_difference(account_list=None, posting_date=None, company=None): from erpnext.stock.utils import get_stock_value_on from erpnext.stock import get_warehouse_account_map if not posting_date: posting_date = nowdate() difference = {} - warehouse_account = get_warehouse_account_map() + warehouse_account = get_warehouse_account_map(company) for warehouse, account_data in iteritems(warehouse_account): if account_data.get('account') in account_list: diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 0a3cd3499b..d1ffd7db66 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -26,7 +26,7 @@ class StockController(AccountsController): delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name) if cint(erpnext.is_perpetual_inventory_enabled(self.company)): - warehouse_account = get_warehouse_account_map() + warehouse_account = get_warehouse_account_map(self.company) if self.docstatus==1: if not gl_entries: @@ -36,7 +36,7 @@ class StockController(AccountsController): if repost_future_gle: items, warehouses = self.get_items_and_warehouses() update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items, - warehouse_account) + warehouse_account, company=self.company) elif self.doctype in ['Purchase Receipt', 'Purchase Invoice'] and self.docstatus == 1: gl_entries = [] gl_entries = self.get_asset_gl_entry(gl_entries) @@ -46,7 +46,7 @@ class StockController(AccountsController): default_cost_center=None): if not warehouse_account: - warehouse_account = get_warehouse_account_map() + warehouse_account = get_warehouse_account_map(self.company) sle_map = self.get_stock_ledger_details() voucher_details = self.get_voucher_details(default_expense_account, default_cost_center, sle_map) @@ -199,7 +199,8 @@ class StockController(AccountsController): def make_adjustment_entry(self, expected_gle, voucher_obj): from erpnext.accounts.utils import get_stock_and_account_difference account_list = [d.account for d in expected_gle] - acc_diff = get_stock_and_account_difference(account_list, expected_gle[0].posting_date) + acc_diff = get_stock_and_account_difference(account_list, + expected_gle[0].posting_date, self.company) cost_center = self.get_company_default("cost_center") stock_adjustment_account = self.get_company_default("stock_adjustment_account") @@ -361,13 +362,13 @@ class StockController(AccountsController): frappe.get_doc("Blanket Order", blanket_order).update_ordered_qty() def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, - warehouse_account=None): + warehouse_account=None, company=None): def _delete_gl_entries(voucher_type, voucher_no): frappe.db.sql("""delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no)) if not warehouse_account: - warehouse_account = get_warehouse_account_map() + warehouse_account = get_warehouse_account_map(company) future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items) gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date) diff --git a/erpnext/patches/v7_0/repost_future_gle_for_purchase_invoice.py b/erpnext/patches/v7_0/repost_future_gle_for_purchase_invoice.py index 22f9db6b2b..9e21fb699b 100644 --- a/erpnext/patches/v7_0/repost_future_gle_for_purchase_invoice.py +++ b/erpnext/patches/v7_0/repost_future_gle_for_purchase_invoice.py @@ -10,14 +10,15 @@ from erpnext.controllers.stock_controller import update_gl_entries_after def execute(): company_list = frappe.db.sql_list("""Select name from tabCompany where enable_perpetual_inventory = 1""") frappe.reload_doc('accounts', 'doctype', 'sales_invoice') - - frappe.reload_doctype("Purchase Invoice") + + frappe.reload_doctype("Purchase Invoice") wh_account = get_warehouse_account_map() - + for pi in frappe.get_all("Purchase Invoice", fields=["name", "company"], filters={"docstatus": 1, "update_stock": 1}): if pi.company in company_list: pi_doc = frappe.get_doc("Purchase Invoice", pi.name) items, warehouses = pi_doc.get_items_and_warehouses() - update_gl_entries_after(pi_doc.posting_date, pi_doc.posting_time, warehouses, items, wh_account) - + update_gl_entries_after(pi_doc.posting_date, pi_doc.posting_time, + warehouses, items, wh_account, company = pi.company) + frappe.db.commit() \ No newline at end of file diff --git a/erpnext/stock/__init__.py b/erpnext/stock/__init__.py index ea3d1036d2..32a03e7373 100644 --- a/erpnext/stock/__init__.py +++ b/erpnext/stock/__init__.py @@ -8,16 +8,21 @@ install_docs = [ {"doctype":"Role", "role_name":"Stock User", "name":"Stock User"}, {"doctype":"Role", "role_name":"Quality Manager", "name":"Quality Manager"}, {"doctype":"Item Group", "item_group_name":"All Item Groups", "is_group": 1}, - {"doctype":"Item Group", "item_group_name":"Default", + {"doctype":"Item Group", "item_group_name":"Default", "parent_item_group":"All Item Groups", "is_group": 0}, ] -def get_warehouse_account_map(): +def get_warehouse_account_map(company=None): if not frappe.flags.warehouse_account_map or frappe.flags.in_test: warehouse_account = frappe._dict() + filters = {} + if company: + filters['company'] = company + for d in frappe.get_all('Warehouse', fields = ["name", "account", "parent_warehouse", "company"], + filters = filters, order_by="lft, rgt"): if not d.account: d.account = get_warehouse_account(d, warehouse_account) @@ -57,6 +62,6 @@ def get_warehouse_account(warehouse, warehouse_account=None): frappe.throw(_("Please set Account in Warehouse {0} or Default Inventory Account in Company {1}") .format(warehouse.name, warehouse.company)) return account - + def get_company_default_inventory_account(company): return frappe.get_cached_value('Company', company, 'default_inventory_account') From 701c0b792ca70d58bb40f7af778f65559b6a46b9 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 8 Mar 2019 12:30:42 +0530 Subject: [PATCH 055/129] feat: Pre mark attendance for Leave Application --- erpnext/hr/doctype/attendance/attendance.json | 35 +++++++++++++++- erpnext/hr/doctype/attendance/attendance.py | 3 +- .../leave_application/leave_application.py | 31 +++++++------- .../test_leave_application.py | 41 ++++++++++++++++++- 4 files changed, 91 insertions(+), 19 deletions(-) diff --git a/erpnext/hr/doctype/attendance/attendance.json b/erpnext/hr/doctype/attendance/attendance.json index 97d28e7e88..2459b7a6df 100644 --- a/erpnext/hr/doctype/attendance/attendance.json +++ b/erpnext/hr/doctype/attendance/attendance.json @@ -218,6 +218,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "leave_application", + "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": "Leave Application", + "length": 0, + "no_copy": 0, + "options": "Leave Application", + "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, @@ -428,7 +461,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-01-30 11:28:13.075959", + "modified": "2019-03-08 12:00:14.043535", "modified_by": "Administrator", "module": "HR", "name": "Attendance", diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index 74a53035ad..7dd9f0e10c 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -40,7 +40,8 @@ class Attendance(Document): def validate_attendance_date(self): date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") - if getdate(self.attendance_date) > getdate(nowdate()): + # leaves can be marked for future dates + if self.status not in ('On Leave', 'Half Day') and getdate(self.attendance_date) > getdate(nowdate()): frappe.throw(_("Attendance can not be marked for future dates")) elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining): frappe.throw(_("Attendance date can not be less than employee's joining date")) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index b85f38b295..819bbf9d20 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -117,28 +117,29 @@ class LeaveApplication(Document): def update_attendance(self): if self.status == "Approved": - attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s\ - and (attendance_date between %s and %s) and docstatus < 2""",(self.employee, self.from_date, self.to_date), as_dict=1) + for dt in daterange(getdate(self.from_date), getdate(self.to_date)): + date = dt.strftime("%Y-%m-%d") + status = "Half Day" if date == self.half_day_date else "On Leave" - if attendance: - for d in attendance: - doc = frappe.get_doc("Attendance", d.name) - if getdate(self.half_day_date) == doc.attendance_date: - status = "Half Day" - else: - status = "On Leave" - frappe.db.sql("""update `tabAttendance` set status = %s, leave_type = %s\ - where name = %s""",(status, self.leave_type, d.name)) + attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee, + attenance_date = date, docstatus = ('!=', 2))) - elif getdate(self.to_date) <= getdate(nowdate()): - for dt in daterange(getdate(self.from_date), getdate(self.to_date)): - date = dt.strftime("%Y-%m-%d") + if attendance_name: + # update existing attendance, change absent to on leave + doc = frappe.get_doc('Attendance', attendance_date) + if doc.status != status: + doc.db_set('status', status) + doc.db_set('leave_type', self.leave_type) + doc.db_set('leave_application', self.name) + else: + # make new attendance and submit it doc = frappe.new_doc("Attendance") doc.employee = self.employee doc.attendance_date = date doc.company = self.company doc.leave_type = self.leave_type - doc.status = "Half Day" if date == self.half_day_date else "On Leave" + doc.leave_application = self.name + doc.status = status doc.flags.ignore_validate = True doc.insert(ignore_permissions=True) doc.submit() diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index a682e8b76f..d3dcca1da0 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -7,7 +7,7 @@ import unittest from erpnext.hr.doctype.leave_application.leave_application import LeaveDayBlockedError, OverlapError, NotAnOptionalHoliday, get_leave_balance_on from frappe.permissions import clear_user_permissions_for_doctype -from frappe.utils import add_days, nowdate, now_datetime +from frappe.utils import add_days, nowdate, now_datetime, getdate test_dependencies = ["Leave Allocation", "Leave Block List"] @@ -67,6 +67,43 @@ class TestLeaveApplication(unittest.TestCase): application.to_date = "2013-01-05" return application + def test_attendance_creation(self): + '''check attendance is automatically created on leave approval''' + make_allocation_record() + application = self.get_application(_test_records[0]) + application.status = 'Approved' + application.from_date = '2018-01-01' + application.to_date = '2018-01-03' + application.insert() + application.submit() + + attendance = frappe.get_all('Attendance', ['name', 'status', 'attendance_date'], dict(leave_application = application.name)) + + # attendance created for all 3 days + self.assertEqual(len(attendance), 3) + + # all on leave + self.assertTrue(all([d.status == 'On Leave' for d in attendance])) + + # dates + dates = [d.attendance_date for d in attendance] + for d in ('2018-01-01', '2018-01-02', '2018-01-03'): + self.assertTrue(getdate(d) in dates) + + def test_overwrite_attendance(self): + # employee marked as absent + doc = frappe.new_doc("Attendance") + doc.employee = '_T-Employee-00001' + doc.attendance_date = '2018-01-01' + doc.company = '_Test Company' + doc.status = 'Absent' + doc.flags.ignore_validate = True + doc.insert(ignore_permissions=True) + doc.submit() + + # now check if the status has been updated + self.test_attendance_creation() + def test_block_list(self): self._clear_roles() @@ -428,7 +465,7 @@ def make_allocation_record(employee=None, leave_type=None): "employee": employee or "_T-Employee-00001", "leave_type": leave_type or "_Test Leave Type", "from_date": "2013-01-01", - "to_date": "2015-12-31", + "to_date": "2019-12-31", "new_leaves_allocated": 30 }) From 96842c9de6f336ce23926e145cfe777bcc1e5559 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 8 Mar 2019 12:36:43 +0530 Subject: [PATCH 056/129] fix(typo): leave_application.py --- erpnext/hr/doctype/leave_application/leave_application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 819bbf9d20..cb0484f58f 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -126,7 +126,7 @@ class LeaveApplication(Document): if attendance_name: # update existing attendance, change absent to on leave - doc = frappe.get_doc('Attendance', attendance_date) + doc = frappe.get_doc('Attendance', attendance_name) if doc.status != status: doc.db_set('status', status) doc.db_set('leave_type', self.leave_type) From 34062291529aee4c26d9c3664f714a32647079f2 Mon Sep 17 00:00:00 2001 From: Frappe Bot Date: Fri, 8 Mar 2019 09:39:32 +0000 Subject: [PATCH 057/129] bumped to version 11.1.14 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index d0ae9c4fc9..4870b194e7 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '11.1.13' +__version__ = '11.1.14' def get_default_company(user=None): '''Get default company for user''' From 1692fbaf1c6a1a2f839ad0b1806c1153c40ba92c Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 8 Mar 2019 18:37:34 +0530 Subject: [PATCH 058/129] fetch from fix --- erpnext/regional/united_arab_emirates/setup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 3c8328b107..250659e54d 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -28,24 +28,24 @@ def make_custom_fields(): purchase_invoice_fields = [ dict(fieldname='company_trn', label='Company TRN', fieldtype='Read Only', insert_after='shipping_address', - options='company.tax_id', print_hide=1), + fetch_from='company.tax_id', print_hide=1), dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic', fieldtype='Read Only', insert_after='supplier_name', - options='supplier.supplier_name_in_arabic', print_hide=1) + fetch_from='supplier.supplier_name_in_arabic', print_hide=1) ] sales_invoice_fields = [ dict(fieldname='company_trn', label='Company TRN', fieldtype='Read Only', insert_after='company_address', - options='company.tax_id', print_hide=1), + fetch_from='company.tax_id', print_hide=1), dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic', fieldtype='Read Only', insert_after='customer_name', - options='customer.customer_name_in_arabic', print_hide=1), + fetch_from='customer.customer_name_in_arabic', print_hide=1), ] invoice_item_fields = [ dict(fieldname='tax_code', label='Tax Code', - fieldtype='Read Only', options='item_code.tax_code', insert_after='description', + fieldtype='Read Only', fetch_from='item_code.tax_code', insert_after='description', allow_on_submit=1, print_hide=1), dict(fieldname='tax_rate', label='Tax Rate', fieldtype='Float', insert_after='tax_code', From 52729bf36934711d2d748f99b73be35fb4651da1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 10 Mar 2019 14:26:30 +0530 Subject: [PATCH 059/129] feat: child table to add multiple time logs in job card --- .../doctype/job_card/job_card.js | 29 ++- .../doctype/job_card/job_card.json | 205 +++++++++++++---- .../doctype/job_card/job_card.py | 109 +++++---- .../doctype/job_card_time_log/__init__.py | 0 .../job_card_time_log/job_card_time_log.json | 208 ++++++++++++++++++ .../job_card_time_log/job_card_time_log.py | 9 + .../doctype/work_order/test_work_order.py | 15 +- erpnext/patches.txt | 1 + .../patches/v11_1/make_job_card_time_logs.py | 29 +++ 9 files changed, 513 insertions(+), 92 deletions(-) create mode 100644 erpnext/manufacturing/doctype/job_card_time_log/__init__.py create mode 100644 erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json create mode 100644 erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.py create mode 100644 erpnext/patches/v11_1/make_job_card_time_logs.py diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index 3fe9b8af30..95549d5a24 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -18,20 +18,27 @@ frappe.ui.form.on('Job Card', { } if (frm.doc.docstatus == 0) { - if (!frm.doc.actual_start_date || !frm.doc.actual_end_date) { - frm.trigger("make_dashboard"); - } + frm.trigger("make_dashboard"); - if (!frm.doc.actual_start_date) { + if (!frm.doc.job_started) { frm.add_custom_button(__("Start Job"), () => { - frm.set_value('actual_start_date', frappe.datetime.now_datetime()); + let row = frappe.model.add_child(frm.doc, 'Job Card Time Log', 'time_logs'); + row.from_time = frappe.datetime.now_datetime(); + frm.set_value('job_started', 1); + frm.set_value('started_time' , row.from_time); frm.save(); }); - } else if (!frm.doc.actual_end_date) { + } else { frm.add_custom_button(__("Complete Job"), () => { - frm.set_value('actual_end_date', frappe.datetime.now_datetime()); - frm.save(); - frm.savesubmit(); + let completed_time = frappe.datetime.now_datetime(); + frm.doc.time_logs.forEach(d => { + if (d.from_time && !d.to_time) { + d.to_time = completed_time; + frm.set_value('started_time' , ''); + frm.set_value('job_started', 0); + frm.save(); + } + }) }); } } @@ -53,8 +60,8 @@ frappe.ui.form.on('Job Card', { var section = frm.dashboard.add_section(timer); - if (frm.doc.actual_start_date) { - let currentIncrement = moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.actual_start_date),"seconds"); + if (frm.doc.started_time) { + let currentIncrement = moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.started_time),"seconds"); initialiseTimer(); function initialiseTimer() { diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json index b020c89053..39c5cce313 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.json +++ b/erpnext/manufacturing/doctype/job_card/job_card.json @@ -21,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "work_order", "fieldtype": "Link", "hidden": 0, @@ -54,6 +55,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "bom_no", "fieldtype": "Link", "hidden": 0, @@ -87,6 +89,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "workstation", "fieldtype": "Link", "hidden": 0, @@ -120,6 +123,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "operation", "fieldtype": "Link", "hidden": 0, @@ -153,6 +157,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_4", "fieldtype": "Column Break", "hidden": 0, @@ -185,6 +190,7 @@ "collapsible": 0, "columns": 0, "default": "Today", + "fetch_if_empty": 0, "fieldname": "posting_date", "fieldtype": "Date", "hidden": 0, @@ -217,6 +223,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "company", "fieldtype": "Link", "hidden": 0, @@ -250,6 +257,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "for_quantity", "fieldtype": "Float", "hidden": 0, @@ -282,6 +290,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "wip_warehouse", "fieldtype": "Link", "hidden": 0, @@ -315,6 +324,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "timing_detail", "fieldtype": "Section Break", "hidden": 0, @@ -347,6 +357,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "employee", "fieldtype": "Link", "hidden": 0, @@ -380,7 +391,74 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "time_in_mins", + "fetch_if_empty": 0, + "fieldname": "time_logs", + "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": "Time Logs", + "length": 0, + "no_copy": 0, + "options": "Job Card Time Log", + "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, + "fetch_if_empty": 0, + "fieldname": "section_break_13", + "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, + "fetch_if_empty": 0, + "fieldname": "total_completed_qty", "fieldtype": "Float", "hidden": 0, "ignore_user_permissions": 0, @@ -389,7 +467,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Time In Mins", + "label": "Total Completed Qty", "length": 0, "no_copy": 0, "permlevel": 0, @@ -412,7 +490,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "column_break_13", + "fetch_if_empty": 0, + "fieldname": "column_break_15", "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, @@ -443,8 +522,9 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "actual_start_date", - "fieldtype": "Datetime", + "fetch_if_empty": 0, + "fieldname": "total_time_in_mins", + "fieldtype": "Float", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -452,46 +532,14 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Actual Start Date", + "label": "Total Time in Mins", "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": "actual_end_date", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Actual End Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, @@ -507,6 +555,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_8", "fieldtype": "Section Break", "hidden": 0, @@ -539,6 +588,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "items", "fieldtype": "Table", "hidden": 0, @@ -572,6 +622,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "more_information", "fieldtype": "Section Break", "hidden": 0, @@ -604,6 +655,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "operation_id", "fieldtype": "Data", "hidden": 1, @@ -637,6 +689,7 @@ "collapsible": 0, "columns": 0, "default": "0", + "fetch_if_empty": 0, "fieldname": "transferred_qty", "fieldtype": "Float", "hidden": 0, @@ -670,6 +723,7 @@ "collapsible": 0, "columns": 0, "default": "0", + "fetch_if_empty": 0, "fieldname": "requested_qty", "fieldtype": "Float", "hidden": 0, @@ -702,6 +756,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "project", "fieldtype": "Link", "hidden": 0, @@ -735,6 +790,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "remarks", "fieldtype": "Small Text", "hidden": 0, @@ -767,6 +823,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_20", "fieldtype": "Column Break", "hidden": 0, @@ -799,6 +856,7 @@ "collapsible": 0, "columns": 0, "default": "Open", + "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", "hidden": 0, @@ -832,6 +890,73 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, + "fieldname": "job_started", + "fieldtype": "Check", + "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": "Job Started", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "started_time", + "fieldtype": "Datetime", + "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": "Started Time", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", "hidden": 0, @@ -868,7 +993,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-12-13 17:23:57.986381", + "modified": "2019-03-10 17:38:37.499871", "modified_by": "Administrator", "module": "Manufacturing", "name": "Job Card", diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index ea9f714fc8..23a4e5105c 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -11,44 +11,56 @@ from frappe.model.document import Document class JobCard(Document): def validate(self): - self.validate_actual_dates() - self.set_time_in_mins() + self.validate_time_logs() self.set_status() - def validate_actual_dates(self): - if get_datetime(self.actual_start_date) > get_datetime(self.actual_end_date): - frappe.throw(_("Actual start date must be less than actual end date")) + def validate_time_logs(self): + self.total_completed_qty = 0.0 + self.total_time_in_mins = 0.0 - if not (self.employee and self.actual_start_date and self.actual_end_date): - return + for d in self.get('time_logs'): + if get_datetime(d.from_time) > get_datetime(d.to_time): + frappe.throw(_("Row {0}: From time must be less than to time").format(d.idx)) - data = frappe.db.sql(""" select name from `tabJob Card` - where - ((%(actual_start_date)s > actual_start_date and %(actual_start_date)s < actual_end_date) or - (%(actual_end_date)s > actual_start_date and %(actual_end_date)s < actual_end_date) or - (%(actual_start_date)s <= actual_start_date and %(actual_end_date)s >= actual_end_date)) and - name != %(name)s and employee = %(employee)s and docstatus =1 - """, { - 'actual_start_date': self.actual_start_date, - 'actual_end_date': self.actual_end_date, - 'employee': self.employee, - 'name': self.name - }, as_dict=1) + data = self.get_overlap_for(d) + if data: + frappe.throw(_("Row {0}: From Time and To Time of {1} is overlapping with {2}") + .format(d.idx, self.name, data.name)) - if data: - frappe.throw(_("Start date and end date is overlapping with the job card {1}") - .format(data[0].name, data[0].name)) + if d.from_time and d.to_time: + d.time_in_mins = time_diff_in_hours(d.to_time, d.from_time) * 60 + self.total_time_in_mins += d.time_in_mins - def set_time_in_mins(self): - if self.actual_start_date and self.actual_end_date: - self.time_in_mins = time_diff_in_hours(self.actual_end_date, self.actual_start_date) * 60 + if d.completed_qty: + self.total_completed_qty += d.completed_qty + + def get_overlap_for(self, args): + existing = frappe.db.sql("""select jc.name as name from + `tabJob Card Time Log` jctl, `tabJob Card` jc where jctl.parent = jc.name and + ( + (%(from_time)s > jctl.from_time and %(from_time)s < jctl.to_time) or + (%(to_time)s > jctl.from_time and %(to_time)s < jctl.to_time) or + (%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time)) + and jctl.name!=%(name)s + and jc.name!=%(parent)s + and jc.docstatus < 2 + and jc.employee = %(employee)s """, + { + "from_time": args.from_time, + "to_time": args.to_time, + "name": args.name or "No Name", + "parent": args.parent or "No Name", + "employee": self.employee + }, as_dict=True) + + return existing[0] if existing else None def get_required_items(self): if not self.get('work_order'): return doc = frappe.get_doc('Work Order', self.get('work_order')) - if doc.transfer_material_against == 'Work Order' and doc.skip_transfer: + if doc.transfer_material_against == 'Work Order' or doc.skip_transfer: return for d in doc.required_items: @@ -67,36 +79,51 @@ class JobCard(Document): }) def on_submit(self): - self.validate_dates() + self.validate_job_card() self.update_work_order() self.set_transferred_qty() - def validate_dates(self): - if not self.actual_start_date and not self.actual_end_date: - frappe.throw(_("Actual start date and actual end date is mandatory")) - def on_cancel(self): self.update_work_order() self.set_transferred_qty() + def validate_job_card(self): + if not self.time_logs: + frappe.throw(_("Time logs are required for job card {0}").format(self.name)) + + if self.total_completed_qty <= 0.0: + frappe.throw(_("Total completed qty must be greater than zero")) + + if self.total_completed_qty > self.for_quantity: + frappe.throw(_("Total completed qty can not be greater than for quantity")) + def update_work_order(self): if not self.work_order: return - data = frappe.db.get_value("Job Card", {'docstatus': 1, 'operation_id': self.operation_id}, - ['sum(time_in_mins)', 'min(actual_start_date)', 'max(actual_end_date)', 'sum(for_quantity)']) + for_quantity, time_in_mins = 0, 0 + from_time_list, to_time_list = [], [] - if data: - time_in_mins, actual_start_date, actual_end_date, for_quantity = data + for d in frappe.get_all('Job Card', + filters = {'docstatus': 1, 'operation_id': self.operation_id}): + doc = frappe.get_doc('Job Card', d.name) + + for_quantity += doc.total_completed_qty + time_in_mins += doc.total_time_in_mins + for time_log in doc.time_logs: + from_time_list.append(time_log.from_time) + to_time_list.append(time_log.to_time) + + if for_quantity: wo = frappe.get_doc('Work Order', self.work_order) for data in wo.operations: if data.name == self.operation_id: data.completed_qty = for_quantity data.actual_operation_time = time_in_mins - data.actual_start_time = actual_start_date - data.actual_end_time = actual_end_date + data.actual_start_time = min(from_time_list) + data.actual_end_time = max(to_time_list) wo.flags.ignore_validate_update_after_submit = True wo.update_operation_status() @@ -132,9 +159,11 @@ class JobCard(Document): break if completed: - job_cards = frappe.get_all('Job Card', filters = {'work_order': self.work_order, + job_cards = frappe.get_all('Job Card', filters = {'work_order': self.work_order, 'docstatus': ('!=', 2)}, fields = 'sum(transferred_qty) as qty', group_by='operation_id') - qty = min([d.qty for d in job_cards]) + + if job_cards: + qty = min([d.qty for d in job_cards]) doc.db_set('material_transferred_for_manufacturing', qty) @@ -147,7 +176,7 @@ class JobCard(Document): 2: "Cancelled" }[self.docstatus or 0] - if self.actual_start_date: + if self.time_logs: self.status = 'Work In Progress' if (self.docstatus == 1 and diff --git a/erpnext/manufacturing/doctype/job_card_time_log/__init__.py b/erpnext/manufacturing/doctype/job_card_time_log/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json b/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json new file mode 100644 index 0000000000..2aab71dee4 --- /dev/null +++ b/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json @@ -0,0 +1,208 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2019-03-08 23:56:43.187569", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "from_time", + "fieldtype": "Datetime", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "From Time", + "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, + "fetch_if_empty": 0, + "fieldname": "to_time", + "fieldtype": "Datetime", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "To Time", + "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, + "fetch_if_empty": 0, + "fieldname": "column_break_2", + "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, + "fetch_if_empty": 0, + "fieldname": "time_in_mins", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Time In Mins", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fetch_if_empty": 0, + "fieldname": "completed_qty", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Completed Qty", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-03-10 17:08:46.504910", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Job Card Time Log", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.py b/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.py new file mode 100644 index 0000000000..3dc6689121 --- /dev/null +++ b/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class JobCardTimeLog(Document): + pass diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 69381c53b3..b292047aa6 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -302,6 +302,19 @@ class TestWorkOrder(unittest.TestCase): self.assertEqual(len(ste.additional_costs), 1) self.assertEqual(ste.total_additional_costs, 1000) + def test_job_card(self): + data = frappe.get_cached_value('BOM', + {'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item']) + + if data: + bom, bom_item = data + + bom_doc = frappe.get_doc('BOM', bom) + work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom) + + job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order}) + self.assertEqual(len(job_cards), len(bom_doc.operations)) + def test_work_order_with_non_transfer_item(self): items = {'Finished Good Transfer Item': 1, '_Test FG Item': 1, '_Test FG Item 1': 0} for item, allow_transfer in items.items(): @@ -346,7 +359,7 @@ def make_wo_order_test_record(**args): wo_order = frappe.new_doc("Work Order") wo_order.production_item = args.production_item or args.item or args.item_code or "_Test FG Item" - wo_order.bom_no = frappe.db.get_value("BOM", {"item": wo_order.production_item, + wo_order.bom_no = args.bom_no or frappe.db.get_value("BOM", {"item": wo_order.production_item, "is_active": 1, "is_default": 1}) wo_order.qty = args.qty or 10 wo_order.wip_warehouse = args.wip_warehouse or "_Test Warehouse - _TC" diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 433141cdf6..7d49ad5fe3 100755 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -588,3 +588,4 @@ execute:frappe.delete_doc('DocType', 'Notification Control') erpnext.patches.v11_0.remove_barcodes_field_from_copy_fields_to_variants erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019 erpnext.patches.v11_0.make_italian_localization_fields # 01-03-2019 +erpnext.patches.v11_1.make_job_card_time_logs \ No newline at end of file diff --git a/erpnext/patches/v11_1/make_job_card_time_logs.py b/erpnext/patches/v11_1/make_job_card_time_logs.py new file mode 100644 index 0000000000..6e708df48d --- /dev/null +++ b/erpnext/patches/v11_1/make_job_card_time_logs.py @@ -0,0 +1,29 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc('manufacturing', 'doctype', 'job_card_time_log') + + if (frappe.db.table_exists("Job Card") + and frappe.get_meta("Job Card").has_field("actual_start_date")): + time_logs = [] + for d in frappe.get_all('Job Card', + fields = ["actual_start_date", "actual_end_date", "time_in_mins", "name", "for_quantity"], + filters = {'docstatus': ("<", 2)}): + if d.actual_start_date: + time_logs.append([d.actual_start_date, d.actual_end_date, d.time_in_mins, + d.for_quantity, d.name, 'Job Card', 'time_logs', frappe.generate_hash("", 10)]) + + if time_logs: + frappe.db.sql(""" INSERT INTO + `tabJob Card Time Log` + (from_time, to_time, time_in_mins, completed_qty, parent, parenttype, parentfield, name) + values {values} + """.format(values = ','.join(['%s'] * len(time_logs))), tuple(time_logs)) + + frappe.reload_doc('manufacturing', 'doctype', 'job_card') + frappe.db.sql(""" update `tabJob Card` set total_completed_qty = for_quantity, + total_time_in_mins = time_in_mins where docstatus < 2 """) \ No newline at end of file From e86ac3c8d4b4d26f5c1ee47232b697138fddc060 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 11 Mar 2019 10:37:28 +0530 Subject: [PATCH 060/129] feat: timesheet Employee Summary Report --- .../employee_billing_summary/__init__.py | 0 .../employee_billing_summary.js | 27 ++++ .../employee_billing_summary.json | 36 ++++++ .../employee_billing_summary.py | 119 ++++++++++++++++++ erpnext/public/node_modules | 1 + 5 files changed, 183 insertions(+) create mode 100644 erpnext/projects/report/employee_billing_summary/__init__.py create mode 100644 erpnext/projects/report/employee_billing_summary/employee_billing_summary.js create mode 100644 erpnext/projects/report/employee_billing_summary/employee_billing_summary.json create mode 100644 erpnext/projects/report/employee_billing_summary/employee_billing_summary.py create mode 120000 erpnext/public/node_modules diff --git a/erpnext/projects/report/employee_billing_summary/__init__.py b/erpnext/projects/report/employee_billing_summary/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js new file mode 100644 index 0000000000..e6e674666d --- /dev/null +++ b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js @@ -0,0 +1,27 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Employee Billing Summary"] = { + "filters": [ + { + fieldname: "employee", + label: __("Employee"), + fieldtype: "Link", + options: "Employee", + }, + { + fieldname:"from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.get_today() + }, + { + fieldname:"to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.add_days(frappe.datetime.get_today(), 30) + }, + + ] +} diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.json b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.json new file mode 100644 index 0000000000..433ebac5dd --- /dev/null +++ b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.json @@ -0,0 +1,36 @@ +{ + "add_total_row": 0, + "creation": "2019-03-08 15:08:19.929728", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2019-03-08 15:08:19.929728", + "modified_by": "Administrator", + "module": "Projects", + "name": "Employee Billing Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Timesheet", + "report_name": "Employee Billing Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Projects User" + }, + { + "role": "HR User" + }, + { + "role": "Manufacturing User" + }, + { + "role": "Employee" + }, + { + "role": "Accounts User" + } + ] +} \ No newline at end of file diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py new file mode 100644 index 0000000000..47323efabe --- /dev/null +++ b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py @@ -0,0 +1,119 @@ +# 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 _ +from frappe.utils import time_diff_in_hours + +def execute(filters=None): + filters = frappe._dict(filters or {}) + print(filters) + + columns = get_columns() + + data = get_data(filters) + return columns, data + +def get_columns(): + return [ + { + "label": _("Employee ID"), + "fieldtype": "Link", + "fieldname": "employee", + "options": "Employee", + "width": 300 + }, + { + "label": _("Employee Name"), + "fieldtype": "data", + "fieldname": "employee_name", + "hidden": 1, + "width": 200 + }, + { + "label": _("Timesheet"), + "fieldtype": "Link", + "fieldname": "timesheet", + "options": "Timesheet", + "width": 150 + }, + { + "label": _("Date"), + "fieldtype": "Date", + "fieldname": "date", + "width": 150 + }, + { + "label": _("Total Billable Hours"), + "fieldtype": "Int", + "fieldname": "total_billable_hours", + "width": 50 + }, + { + "label": _("Total Hours"), + "fieldtype": "Int", + "fieldname": "total_hours", + "width": 50 + }, + { + "label": _("Amount"), + "fieldtype": "Int", + "fieldname": "amount", + "width": 50 + } + ] + +def get_data(filters): + data = [] + if "employee" in filters: + record= frappe.db.sql('''SELECT + employee, employee_name, name, total_billable_hours, total_hours, total_billable_amount + FROM + `tabTimesheet` + WHERE + employee = %s and (start_date <= %s and end_date >= %s)''',(filters.employee, filters.to_date, filters.from_date), + as_dict=1 + ) + for entries in record: + + timesheet_details = frappe.get_all( + "Timesheet Detail", + filters={"parent": entries.name}, + fields=["*"] + ) + + total_hours = 0 + total_billable_hours = 0 + print("-------------------------------------------->>>>>>>") + for time in timesheet_details: + time_start = time.from_time + time_end = frappe.utils.add_to_date(time.from_time, hours=time.hours) + + from_date = frappe.utils.get_datetime(filters.from_date) + to_date = frappe.utils.get_datetime(filters.to_date) + + if time_start <= from_date and time_end <= to_date: + total_hours += abs(time_diff_in_hours(time_end, from_date)) + print(from_date, time_end) + print("case 1", entries.name,time_diff_in_hours(time_end, from_date)) + elif time_start >= from_date and time_end >= to_date: + total_hours += abs(time_diff_in_hours(to_date, time_start)) + print(time_start, to_date) + print("case 2", entries.name,time_diff_in_hours(to_date, time_start)) + elif time_start >= from_date and time_end <= to_date: + total_hours = entries.total_hours + print("case 3 all set", entries.name) + + print(total_hours) + print("-------------------------------------------->>>>>>>") + row = { + "employee": entries.employee, + "employee_name": entries.employee_name, + "timesheet": entries.name, + "total_billable_hours": entries.total_billable_hours, + "total_hours": entries.total_hours, + "amount": 1 + } + data.append(row) + return data \ No newline at end of file diff --git a/erpnext/public/node_modules b/erpnext/public/node_modules new file mode 120000 index 0000000000..229573e057 --- /dev/null +++ b/erpnext/public/node_modules @@ -0,0 +1 @@ +/Users/anuragmishra/test/apps/erpnext/node_modules \ No newline at end of file From 59f4556d95213c6d6ea9c99869ba0724b8855fdb Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 11 Mar 2019 11:46:01 +0530 Subject: [PATCH 061/129] Commonify code --- .../employee_billing_summary.js | 1 - .../employee_billing_summary.py | 35 ++++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js index e6e674666d..b792e818d8 100644 --- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js +++ b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js @@ -22,6 +22,5 @@ frappe.query_reports["Employee Billing Summary"] = { fieldtype: "Date", default: frappe.datetime.add_days(frappe.datetime.get_today(), 30) }, - ] } diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py index 47323efabe..491fa764d9 100644 --- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py +++ b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py @@ -8,8 +8,6 @@ from frappe.utils import time_diff_in_hours def execute(filters=None): filters = frappe._dict(filters or {}) - print(filters) - columns = get_columns() data = get_data(filters) @@ -85,7 +83,8 @@ def get_data(filters): total_hours = 0 total_billable_hours = 0 - print("-------------------------------------------->>>>>>>") + total_amount = 0 + for time in timesheet_details: time_start = time.from_time time_end = frappe.utils.add_to_date(time.from_time, hours=time.hours) @@ -94,26 +93,28 @@ def get_data(filters): to_date = frappe.utils.get_datetime(filters.to_date) if time_start <= from_date and time_end <= to_date: - total_hours += abs(time_diff_in_hours(time_end, from_date)) - print(from_date, time_end) - print("case 1", entries.name,time_diff_in_hours(time_end, from_date)) + total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(time, time_end, from_date, total_hours, total_billable_hours, total_amount) elif time_start >= from_date and time_end >= to_date: - total_hours += abs(time_diff_in_hours(to_date, time_start)) - print(time_start, to_date) - print("case 2", entries.name,time_diff_in_hours(to_date, time_start)) + total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(time, to_date, time_start, total_hours, total_billable_hours, total_amount) elif time_start >= from_date and time_end <= to_date: - total_hours = entries.total_hours - print("case 3 all set", entries.name) + total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(time, time_end, time_start, total_hours, total_billable_hours, total_amount) - print(total_hours) - print("-------------------------------------------->>>>>>>") row = { "employee": entries.employee, "employee_name": entries.employee_name, "timesheet": entries.name, - "total_billable_hours": entries.total_billable_hours, - "total_hours": entries.total_hours, - "amount": 1 + "total_billable_hours": total_billable_hours, + "total_hours": total_hours, + "amount": total_amount } + data.append(row) - return data \ No newline at end of file + return data + +def get_billable_and_total_hours(time, end, start, total_hours, total_billable_hours, total_amount): + total_hours += abs(time_diff_in_hours(end, start)) + if time.billable: + total_billable_hours += abs(time_diff_in_hours(end, start)) + total_amount += total_billable_hours * time.billing_rate + + return total_hours, total_billable_hours, total_amount From 9b64baa7342b3ba01c427837a9516c3512301d0f Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 11 Mar 2019 12:02:25 +0530 Subject: [PATCH 062/129] Minor Fixes --- erpnext/public/node_modules | 1 - 1 file changed, 1 deletion(-) delete mode 120000 erpnext/public/node_modules diff --git a/erpnext/public/node_modules b/erpnext/public/node_modules deleted file mode 120000 index 229573e057..0000000000 --- a/erpnext/public/node_modules +++ /dev/null @@ -1 +0,0 @@ -/Users/anuragmishra/test/apps/erpnext/node_modules \ No newline at end of file From 267723f57ba1f78c30cda16018ecff0888450138 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 11 Mar 2019 14:06:00 +0530 Subject: [PATCH 063/129] fix: stock ledgre report not working --- erpnext/stock/report/stock_ledger/stock_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index 0bb0b236f9..46c55d2ff3 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -113,7 +113,7 @@ def get_item_details(items, sl_entries, include_uom): cf_join = "left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom='%s'" \ % frappe.db.escape(include_uom) - item_codes = ', '.join(['"' + frappe.db.escape(i, percent=False) + '"' for i in items]) + item_codes = ', '.join([frappe.db.escape(i, percent=False) for i in items]) res = frappe.db.sql(""" select item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom {cf_field} From 7c3e017ac35b46cb0ebd56ab193f38300f85824b Mon Sep 17 00:00:00 2001 From: pawan Date: Mon, 11 Mar 2019 14:47:57 +0530 Subject: [PATCH 064/129] fix: Amazon integration issues --- .../amazon_mws_settings/amazon_methods.py | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py index c21f11ead7..9afa32b9df 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py @@ -162,6 +162,8 @@ def create_item_code(amazon_item_json, sku): igroup.parent_item_group = mws_settings.item_group igroup.insert() + item.append("item_defaults", {'company':mws_settings.company}) + item.insert(ignore_permissions=True) create_item_price(amazon_item_json, item.item_code) @@ -213,7 +215,7 @@ def get_orders(after_date): fulfillment_channels=["MFN", "AFN"], lastupdatedafter=after_date, orderstatus=statuses, - max_results='20') + max_results='50') while True: orders_list = [] @@ -432,8 +434,7 @@ def get_order_items(market_place_order_id): return final_order_items def get_item_code(order_item): - asin = order_item.ASIN - item_code = frappe.db.get_value("Item", {"amazon_item_code": asin}, "item_code") + item_code = frappe.db.get_value("Item", {"item_code": sku}, "item_code") if item_code: return item_code @@ -451,11 +452,16 @@ def get_charges_and_fees(market_place_order_id): shipment_item_list = return_as_list(shipment_event.ShipmentEvent.ShipmentItemList.ShipmentItem) for shipment_item in shipment_item_list: - charges = return_as_list(shipment_item.ItemChargeList.ChargeComponent) - fees = return_as_list(shipment_item.ItemFeeList.FeeComponent) + charges, fees = [] + + if 'ItemChargeList' in shipment_item.keys(): + charges = return_as_list(shipment_item.ItemChargeList.ChargeComponent) + + if 'ItemFeeList' in shipment_item.keys(): + fees = return_as_list(shipment_item.ItemFeeList.FeeComponent) for charge in charges: - if(charge.ChargeType != "Principal"): + if(charge.ChargeType != "Principal") and float(charge.ChargeAmount.CurrencyAmount) != 0: charge_account = get_account(charge.ChargeType) charges_fees.get("charges").append({ "charge_type":"Actual", @@ -465,13 +471,14 @@ def get_charges_and_fees(market_place_order_id): }) for fee in fees: - fee_account = get_account(fee.FeeType) - charges_fees.get("fees").append({ - "charge_type":"Actual", - "account_head": fee_account, - "tax_amount": fee.FeeAmount.CurrencyAmount, - "description": fee.FeeType + " for " + shipment_item.SellerSKU - }) + if float(fee.FeeAmount.CurrencyAmount) != 0: + fee_account = get_account(fee.FeeType) + charges_fees.get("fees").append({ + "charge_type":"Actual", + "account_head": fee_account, + "tax_amount": fee.FeeAmount.CurrencyAmount, + "description": fee.FeeType + " for " + shipment_item.SellerSKU + }) return charges_fees From c0e1000919257b8ddc9f8f3425c260f9e15320a8 Mon Sep 17 00:00:00 2001 From: pawan Date: Mon, 11 Mar 2019 14:58:17 +0530 Subject: [PATCH 065/129] fix: SKU issue --- .../doctype/amazon_mws_settings/amazon_methods.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py index 9afa32b9df..1c39d8818c 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py @@ -434,6 +434,7 @@ def get_order_items(market_place_order_id): return final_order_items def get_item_code(order_item): + sku = order_item.SellerSKU item_code = frappe.db.get_value("Item", {"item_code": sku}, "item_code") if item_code: return item_code From 121825517815ffa9f77475bfd10e9dffa5cd2f58 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 11 Mar 2019 17:43:44 +0530 Subject: [PATCH 066/129] feat: Project Billing Summary for timesheet --- erpnext/projects/report/billing_summary.py | 137 ++++++++++++++++++ .../employee_billing_summary.py | 110 +------------- .../project_billing_summary/__init__.py | 0 .../project_billing_summary.js | 26 ++++ .../project_billing_summary.json | 36 +++++ .../project_billing_summary.py | 14 ++ 6 files changed, 215 insertions(+), 108 deletions(-) create mode 100644 erpnext/projects/report/billing_summary.py create mode 100644 erpnext/projects/report/project_billing_summary/__init__.py create mode 100644 erpnext/projects/report/project_billing_summary/project_billing_summary.js create mode 100644 erpnext/projects/report/project_billing_summary/project_billing_summary.json create mode 100644 erpnext/projects/report/project_billing_summary/project_billing_summary.py diff --git a/erpnext/projects/report/billing_summary.py b/erpnext/projects/report/billing_summary.py new file mode 100644 index 0000000000..e34e90b1a4 --- /dev/null +++ b/erpnext/projects/report/billing_summary.py @@ -0,0 +1,137 @@ +# 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 _ +from frappe.utils import time_diff_in_hours + + +def get_columns(): + return [ + { + "label": _("Employee ID"), + "fieldtype": "Link", + "fieldname": "employee", + "options": "Employee", + "width": 300 + }, + { + "label": _("Employee Name"), + "fieldtype": "data", + "fieldname": "employee_name", + "hidden": 1, + "width": 200 + }, + { + "label": _("Timesheet"), + "fieldtype": "Link", + "fieldname": "timesheet", + "options": "Timesheet", + "width": 150 + }, + { + "label": _("Total Billable Hours"), + "fieldtype": "Int", + "fieldname": "total_billable_hours", + "width": 50 + }, + { + "label": _("Total Hours"), + "fieldtype": "Int", + "fieldname": "total_hours", + "width": 50 + }, + { + "label": _("Amount"), + "fieldtype": "Int", + "fieldname": "amount", + "width": 100 + } + ] + +def get_data(filters): + data = [] + + if "employee" in filters: + record= frappe.db.sql('''SELECT + employee, employee_name, name, total_billable_hours, total_hours, total_billable_amount + FROM + `tabTimesheet` + WHERE + employee = %s and (start_date <= %s and end_date >= %s)''',(filters.employee, filters.to_date, filters.from_date), + as_dict=1 + ) + + elif "project" in filters: + record= frappe.db.sql('''SELECT + employee, employee_name, name, total_billable_hours, total_hours, total_billable_amount + FROM + `tabTimesheet` + WHERE + start_date <= %s and end_date >= %s''',(filters.to_date, filters.from_date), + as_dict=1 + ) + else: + record = {} + + + for entries in record: + + timesheet_details_filter = {"parent": entries.name} + + if "project" in filters: + timesheet_details_filter["project"] = filters.project + + timesheet_details = frappe.get_all( + "Timesheet Detail", + filters = timesheet_details_filter, + fields=["*"] + ) + + total_hours = 0 + total_billable_hours = 0 + total_amount = 0 + check_entries = False + + for time in timesheet_details: + + check_entries = True + + time_start = time.from_time + time_end = frappe.utils.add_to_date(time.from_time, hours=time.hours) + + from_date = frappe.utils.get_datetime(filters.from_date) + to_date = frappe.utils.get_datetime(filters.to_date) + + if time_start <= from_date and time_end <= to_date: + total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(time, time_end, from_date, total_hours, total_billable_hours, total_amount) + elif time_start >= from_date and time_end >= to_date: + total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(time, to_date, time_start, total_hours, total_billable_hours, total_amount) + elif time_start >= from_date and time_end <= to_date: + total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(time, time_end, time_start, total_hours, total_billable_hours, total_amount) + + row = { + "employee": entries.employee, + "employee_name": entries.employee_name, + "timesheet": entries.name, + "total_billable_hours": total_billable_hours, + "total_hours": total_hours, + "amount": total_amount + } + + if check_entries: + data.append(row) + check_entries = False + + return data + + +def get_billable_and_total_hours(time, end, start, total_hours, total_billable_hours, total_amount): + total_hours += abs(time_diff_in_hours(end, start)) + if time.billable: + total_billable_hours += abs(time_diff_in_hours(end, start)) + total_amount += total_billable_hours * time.billing_rate + + return total_hours, total_billable_hours, total_amount \ No newline at end of file diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py index 491fa764d9..cd5ad7803a 100644 --- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py +++ b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py @@ -4,117 +4,11 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import time_diff_in_hours +from erpnext.projects.report.billing_summary import get_columns, get_data def execute(filters=None): filters = frappe._dict(filters or {}) columns = get_columns() data = get_data(filters) - return columns, data - -def get_columns(): - return [ - { - "label": _("Employee ID"), - "fieldtype": "Link", - "fieldname": "employee", - "options": "Employee", - "width": 300 - }, - { - "label": _("Employee Name"), - "fieldtype": "data", - "fieldname": "employee_name", - "hidden": 1, - "width": 200 - }, - { - "label": _("Timesheet"), - "fieldtype": "Link", - "fieldname": "timesheet", - "options": "Timesheet", - "width": 150 - }, - { - "label": _("Date"), - "fieldtype": "Date", - "fieldname": "date", - "width": 150 - }, - { - "label": _("Total Billable Hours"), - "fieldtype": "Int", - "fieldname": "total_billable_hours", - "width": 50 - }, - { - "label": _("Total Hours"), - "fieldtype": "Int", - "fieldname": "total_hours", - "width": 50 - }, - { - "label": _("Amount"), - "fieldtype": "Int", - "fieldname": "amount", - "width": 50 - } - ] - -def get_data(filters): - data = [] - if "employee" in filters: - record= frappe.db.sql('''SELECT - employee, employee_name, name, total_billable_hours, total_hours, total_billable_amount - FROM - `tabTimesheet` - WHERE - employee = %s and (start_date <= %s and end_date >= %s)''',(filters.employee, filters.to_date, filters.from_date), - as_dict=1 - ) - for entries in record: - - timesheet_details = frappe.get_all( - "Timesheet Detail", - filters={"parent": entries.name}, - fields=["*"] - ) - - total_hours = 0 - total_billable_hours = 0 - total_amount = 0 - - for time in timesheet_details: - time_start = time.from_time - time_end = frappe.utils.add_to_date(time.from_time, hours=time.hours) - - from_date = frappe.utils.get_datetime(filters.from_date) - to_date = frappe.utils.get_datetime(filters.to_date) - - if time_start <= from_date and time_end <= to_date: - total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(time, time_end, from_date, total_hours, total_billable_hours, total_amount) - elif time_start >= from_date and time_end >= to_date: - total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(time, to_date, time_start, total_hours, total_billable_hours, total_amount) - elif time_start >= from_date and time_end <= to_date: - total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(time, time_end, time_start, total_hours, total_billable_hours, total_amount) - - row = { - "employee": entries.employee, - "employee_name": entries.employee_name, - "timesheet": entries.name, - "total_billable_hours": total_billable_hours, - "total_hours": total_hours, - "amount": total_amount - } - - data.append(row) - return data - -def get_billable_and_total_hours(time, end, start, total_hours, total_billable_hours, total_amount): - total_hours += abs(time_diff_in_hours(end, start)) - if time.billable: - total_billable_hours += abs(time_diff_in_hours(end, start)) - total_amount += total_billable_hours * time.billing_rate - - return total_hours, total_billable_hours, total_amount + return columns, data \ No newline at end of file diff --git a/erpnext/projects/report/project_billing_summary/__init__.py b/erpnext/projects/report/project_billing_summary/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.js b/erpnext/projects/report/project_billing_summary/project_billing_summary.js new file mode 100644 index 0000000000..18dbbd19bb --- /dev/null +++ b/erpnext/projects/report/project_billing_summary/project_billing_summary.js @@ -0,0 +1,26 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Project Billing Summary"] = { + "filters": [ + { + fieldname: "project", + label: __("Project"), + fieldtype: "Link", + options: "Project", + }, + { + fieldname:"from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.get_today() + }, + { + fieldname:"to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.add_days(frappe.datetime.get_today(), 30) + }, + ] +} diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.json b/erpnext/projects/report/project_billing_summary/project_billing_summary.json new file mode 100644 index 0000000000..a3f91c802d --- /dev/null +++ b/erpnext/projects/report/project_billing_summary/project_billing_summary.json @@ -0,0 +1,36 @@ +{ + "add_total_row": 0, + "creation": "2019-03-11 16:22:39.460524", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2019-03-11 16:22:39.460524", + "modified_by": "Administrator", + "module": "Projects", + "name": "Project Billing Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Timesheet", + "report_name": "Project Billing Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Projects User" + }, + { + "role": "HR User" + }, + { + "role": "Manufacturing User" + }, + { + "role": "Employee" + }, + { + "role": "Accounts User" + } + ] +} \ No newline at end of file diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.py b/erpnext/projects/report/project_billing_summary/project_billing_summary.py new file mode 100644 index 0000000000..cd5ad7803a --- /dev/null +++ b/erpnext/projects/report/project_billing_summary/project_billing_summary.py @@ -0,0 +1,14 @@ +# 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 _ +from erpnext.projects.report.billing_summary import get_columns, get_data + +def execute(filters=None): + filters = frappe._dict(filters or {}) + columns = get_columns() + + data = get_data(filters) + return columns, data \ No newline at end of file From 34e1f92a7e80f94673b54a5a48dcd37a320a421a Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 11 Mar 2019 18:17:22 +0530 Subject: [PATCH 067/129] fix: readability --- erpnext/projects/report/billing_summary.py | 104 +++++++++++---------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/erpnext/projects/report/billing_summary.py b/erpnext/projects/report/billing_summary.py index e34e90b1a4..cbea5f5c40 100644 --- a/erpnext/projects/report/billing_summary.py +++ b/erpnext/projects/report/billing_summary.py @@ -54,63 +54,37 @@ def get_columns(): def get_data(filters): data = [] - if "employee" in filters: - record= frappe.db.sql('''SELECT - employee, employee_name, name, total_billable_hours, total_hours, total_billable_amount - FROM - `tabTimesheet` - WHERE - employee = %s and (start_date <= %s and end_date >= %s)''',(filters.employee, filters.to_date, filters.from_date), - as_dict=1 - ) - - elif "project" in filters: - record= frappe.db.sql('''SELECT - employee, employee_name, name, total_billable_hours, total_hours, total_billable_amount - FROM - `tabTimesheet` - WHERE - start_date <= %s and end_date >= %s''',(filters.to_date, filters.from_date), - as_dict=1 - ) - else: - record = {} - + record = get_records(filters) for entries in record: - - timesheet_details_filter = {"parent": entries.name} - - if "project" in filters: - timesheet_details_filter["project"] = filters.project - - timesheet_details = frappe.get_all( - "Timesheet Detail", - filters = timesheet_details_filter, - fields=["*"] - ) - total_hours = 0 total_billable_hours = 0 total_amount = 0 - check_entries = False + entries_exists = False - for time in timesheet_details: + timesheet_details = get_timesheet_details(filters, entries.name) - check_entries = True + for activity in timesheet_details: - time_start = time.from_time - time_end = frappe.utils.add_to_date(time.from_time, hours=time.hours) + entries_exists = True + + time_start = activity.from_time + time_end = frappe.utils.add_to_date(activity.from_time, hours=activity.hours) from_date = frappe.utils.get_datetime(filters.from_date) to_date = frappe.utils.get_datetime(filters.to_date) if time_start <= from_date and time_end <= to_date: - total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(time, time_end, from_date, total_hours, total_billable_hours, total_amount) + total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(activity, + time_end, from_date, total_hours, total_billable_hours, total_amount) + elif time_start >= from_date and time_end >= to_date: - total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(time, to_date, time_start, total_hours, total_billable_hours, total_amount) + total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(activity, + to_date, time_start, total_hours, total_billable_hours, total_amount) + elif time_start >= from_date and time_end <= to_date: - total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(time, time_end, time_start, total_hours, total_billable_hours, total_amount) + total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(activity, + time_end, time_start, total_hours, total_billable_hours, total_amount) row = { "employee": entries.employee, @@ -121,17 +95,51 @@ def get_data(filters): "amount": total_amount } - if check_entries: + if entries_exists: data.append(row) - check_entries = False + entries_exists = False return data +def get_records(filters): + if "employee" in filters: + return frappe.db.sql('''SELECT + employee, employee_name, name, total_billable_hours, total_hours, total_billable_amount + FROM + `tabTimesheet` + WHERE + employee = %s and (start_date <= %s and end_date >= %s)''', + (filters.employee, filters.to_date, filters.from_date), + as_dict=1 + ) -def get_billable_and_total_hours(time, end, start, total_hours, total_billable_hours, total_amount): + elif "project" in filters: + return frappe.db.sql('''SELECT + employee, employee_name, name, total_billable_hours, total_hours, total_billable_amount + FROM + `tabTimesheet` + WHERE + start_date <= %s and end_date >= %s''',(filters.to_date, filters.from_date), + as_dict=1 + ) + else: + return {} + +def get_billable_and_total_hours(activity, end, start, total_hours, total_billable_hours, total_amount): total_hours += abs(time_diff_in_hours(end, start)) - if time.billable: + if activity.billable: total_billable_hours += abs(time_diff_in_hours(end, start)) - total_amount += total_billable_hours * time.billing_rate + total_amount += total_billable_hours * activity.billing_rate + return total_hours, total_billable_hours, total_amount - return total_hours, total_billable_hours, total_amount \ No newline at end of file +def get_timesheet_details(filters, parent): + timesheet_details_filter = {"parent": parent} + + if "project" in filters: + timesheet_details_filter["project"] = filters.project + + return frappe.get_all( + "Timesheet Detail", + filters = timesheet_details_filter, + fields=["*"] + ) \ No newline at end of file From 22211511320b2fe9102241e8a76aa92cbebc18bb Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 12 Mar 2019 10:55:11 +0530 Subject: [PATCH 068/129] fix: Update Training Level --- erpnext/hr/doctype/training_event/training_event.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/training_event/training_event.json b/erpnext/hr/doctype/training_event/training_event.json index 4b812a992e..527ac1bc8f 100644 --- a/erpnext/hr/doctype/training_event/training_event.json +++ b/erpnext/hr/doctype/training_event/training_event.json @@ -214,7 +214,7 @@ "label": "Level", "length": 0, "no_copy": 0, - "options": "\nBeginner\nExpert\nAdvance", + "options": "\nBeginner\nIntermediate\nAdvance", "permlevel": 0, "precision": "", "print_hide": 0, @@ -847,4 +847,4 @@ "title_field": "event_name", "track_changes": 0, "track_seen": 0 -} \ No newline at end of file +} From 175709ed5070ebd1b61394e42b39e84595cf507e Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 12 Mar 2019 10:55:39 +0530 Subject: [PATCH 069/129] fix: Update Training Level - Rename Expert to Intermediate, since it was ambiguous with Advance --- erpnext/hr/doctype/training_event/training_event.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/training_event/training_event.json b/erpnext/hr/doctype/training_event/training_event.json index 4b812a992e..527ac1bc8f 100644 --- a/erpnext/hr/doctype/training_event/training_event.json +++ b/erpnext/hr/doctype/training_event/training_event.json @@ -214,7 +214,7 @@ "label": "Level", "length": 0, "no_copy": 0, - "options": "\nBeginner\nExpert\nAdvance", + "options": "\nBeginner\nIntermediate\nAdvance", "permlevel": 0, "precision": "", "print_hide": 0, @@ -847,4 +847,4 @@ "title_field": "event_name", "track_changes": 0, "track_seen": 0 -} \ No newline at end of file +} From 47f1080f1e4e819c88c94fd87489c7a08e2eaa21 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 12 Mar 2019 10:57:35 +0530 Subject: [PATCH 070/129] fix: Update modified --- erpnext/hr/doctype/training_event/training_event.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/training_event/training_event.json b/erpnext/hr/doctype/training_event/training_event.json index 527ac1bc8f..fcf845a587 100644 --- a/erpnext/hr/doctype/training_event/training_event.json +++ b/erpnext/hr/doctype/training_event/training_event.json @@ -809,7 +809,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-10-23 06:13:29.065781", + "modified": "2019-03-12 10:56:29.065781", "modified_by": "Administrator", "module": "HR", "name": "Training Event", From 9a1adc7c4937d664e7227144672d53a72b9d1776 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 12 Mar 2019 11:00:02 +0530 Subject: [PATCH 071/129] Added mandatory feilds to the report --- .../employee_billing_summary/employee_billing_summary.js | 7 +++++-- .../project_billing_summary/project_billing_summary.js | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js index b792e818d8..65c2a690cf 100644 --- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js +++ b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js @@ -9,18 +9,21 @@ frappe.query_reports["Employee Billing Summary"] = { label: __("Employee"), fieldtype: "Link", options: "Employee", + reqd: 1 }, { fieldname:"from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.datetime.get_today() + default: frappe.datetime.get_today(), + reqd: 1 }, { fieldname:"to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.datetime.add_days(frappe.datetime.get_today(), 30) + default: frappe.datetime.add_days(frappe.datetime.get_today(), 30), + reqd: 1 }, ] } diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.js b/erpnext/projects/report/project_billing_summary/project_billing_summary.js index 18dbbd19bb..62362c35cf 100644 --- a/erpnext/projects/report/project_billing_summary/project_billing_summary.js +++ b/erpnext/projects/report/project_billing_summary/project_billing_summary.js @@ -9,18 +9,21 @@ frappe.query_reports["Project Billing Summary"] = { label: __("Project"), fieldtype: "Link", options: "Project", + reqd: 1 }, { fieldname:"from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.datetime.get_today() + default: frappe.datetime.get_today(), + reqd: 1 }, { fieldname:"to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.datetime.add_days(frappe.datetime.get_today(), 30) + default: frappe.datetime.add_days(frappe.datetime.get_today(), 30), + reqd: 1 }, ] } From b4745ec6683075e7d077984eb7b614a0c755bd6b Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Mon, 4 Feb 2019 17:52:51 +0530 Subject: [PATCH 072/129] fix(work_order): Change Work Order title to show item name instead of item code --- .../doctype/work_order/work_order.js | 5 +- .../doctype/work_order/work_order.json | 3275 +++++++++-------- .../doctype/work_order/work_order.py | 7 +- erpnext/patches.txt | 1 + .../v12_0/add_item_name_in_work_orders.py | 13 + 5 files changed, 1674 insertions(+), 1627 deletions(-) mode change 100644 => 100755 erpnext/patches.txt create mode 100644 erpnext/patches/v12_0/add_item_name_in_work_orders.py diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 8a8a6c900c..8ce6154656 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -303,8 +303,9 @@ frappe.ui.form.on("Work Order", { frm.set_value('sales_order', ""); frm.trigger('set_sales_order'); erpnext.in_production_item_onchange = true; - $.each(["description", "stock_uom", "project", "bom_no", - "allow_alternative_item", "transfer_material_against"], function(i, field) { + + $.each(["description", "stock_uom", "project", "bom_no", "allow_alternative_item", + "transfer_material_against", "item_name"], function(i, field) { frm.set_value(field, r.message[field]); }); diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index a65d04f61b..f22697fe27 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -1,1732 +1,1765 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2013-01-10 16:34:16", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "naming_series:", + "beta": 0, + "creation": "2013-01-10 16:34:16", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item", - "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, - "options": "fa fa-gift", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "item", + "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, + "options": "fa fa-gift", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 0, - "options": "MFG-WO-.YYYY.-", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "fieldname": "naming_series", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Series", + "length": 0, + "no_copy": 0, + "options": "MFG-WO-.YYYY.-", + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Draft", - "depends_on": "eval:!doc.__islocal", - "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": 0, - "label": "Status", - "length": 0, - "no_copy": 1, - "oldfieldname": "status", - "oldfieldtype": "Select", - "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "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, + "default": "Draft", + "depends_on": "eval:!doc.__islocal", + "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": 0, + "label": "Status", + "length": 0, + "no_copy": 1, + "oldfieldname": "status", + "oldfieldtype": "Select", + "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 1, + "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": "production_item", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Item To Manufacture", - "length": 0, - "no_copy": 0, - "oldfieldname": "production_item", - "oldfieldtype": "Link", - "options": "Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "production_item", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Item To Manufacture", + "length": 0, + "no_copy": 0, + "oldfieldname": "production_item", + "oldfieldtype": "Link", + "options": "Item", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "", - "fieldname": "bom_no", - "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": "BOM No", - "length": 0, - "no_copy": 0, - "oldfieldname": "bom_no", - "oldfieldtype": "Link", - "options": "BOM", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.production_item", + "fieldname": "item_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Item Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "allow_alternative_item", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Alternative Item", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "description": "", + "fieldname": "bom_no", + "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": "BOM No", + "length": 0, + "no_copy": 0, + "oldfieldname": "bom_no", + "oldfieldtype": "Link", + "options": "BOM", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "description": "Plan material for sub-assemblies", - "fieldname": "use_multi_level_bom", - "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": "Use Multi-Level BOM", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "allow_alternative_item", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Allow Alternative Item", + "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": "Check if material transfer entry is not required", - "fieldname": "skip_transfer", - "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": "Skip Material Transfer", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "description": "Plan material for sub-assemblies", + "fieldname": "use_multi_level_bom", + "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": "Use Multi-Level BOM", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "Check if material transfer entry is not required", + "fieldname": "skip_transfer", + "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": "Skip Material Transfer", + "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": "column_break1", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "oldfieldtype": "Column Break", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0, "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "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": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 1, + "report_hide": 0, + "reqd": 1, + "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": "qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Qty To Manufacture", - "length": 0, - "no_copy": 0, - "oldfieldname": "qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fieldname": "qty", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Qty To Manufacture", + "length": 0, + "no_copy": 0, + "oldfieldname": "qty", + "oldfieldtype": "Currency", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "eval:doc.docstatus==1 && doc.skip_transfer==0", - "description": "", - "fieldname": "material_transferred_for_manufacturing", - "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": "Material Transferred for Manufacturing", - "length": 0, - "no_copy": 1, - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "depends_on": "eval:doc.docstatus==1 && doc.skip_transfer==0", + "description": "", + "fieldname": "material_transferred_for_manufacturing", + "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": "Material Transferred for Manufacturing", + "length": 0, + "no_copy": 1, + "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, - "default": "0", - "depends_on": "eval:doc.docstatus==1", - "description": "", - "fieldname": "produced_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Manufactured Qty", - "length": 0, - "no_copy": 1, - "oldfieldname": "produced_qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "depends_on": "eval:doc.docstatus==1", + "description": "", + "fieldname": "produced_qty", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Manufactured Qty", + "length": 0, + "no_copy": 1, + "oldfieldname": "produced_qty", + "oldfieldtype": "Currency", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "sales_order", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Order", - "length": 0, - "no_copy": 0, - "options": "Sales Order", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "", + "fieldname": "sales_order", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 1, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Sales Order", + "length": 0, + "no_copy": 0, + "options": "Sales Order", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "project", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Project", - "length": 0, - "no_copy": 0, - "oldfieldname": "project", - "oldfieldtype": "Link", - "options": "Project", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "project", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Project", + "length": 0, + "no_copy": 0, + "oldfieldname": "project", + "oldfieldtype": "Link", + "options": "Project", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "skip_transfer", - "fieldname": "from_wip_warehouse", - "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": "Backflush raw materials from work-in-progress warehouse", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "skip_transfer", + "fieldname": "from_wip_warehouse", + "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": "Backflush raw materials from work-in-progress warehouse", + "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": "warehouses", - "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": "Warehouses", - "length": 0, - "no_copy": 0, - "options": "fa fa-building", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "warehouses", + "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": "Warehouses", + "length": 0, + "no_copy": 0, + "options": "fa fa-building", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "wip_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": "Work-in-Progress Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "wip_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": "Work-in-Progress Warehouse", + "length": 0, + "no_copy": 0, + "options": "Warehouse", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "", - "fieldname": "fg_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": "Target Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "description": "", + "fieldname": "fg_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": "Target Warehouse", + "length": 0, + "no_copy": 0, + "options": "Warehouse", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_12", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_12", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "scrap_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": "Scrap 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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "scrap_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": "Scrap 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": "required_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": "Required 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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "required_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": "Required 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, - "fieldname": "required_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": "Required Items", - "length": 0, - "no_copy": 1, - "options": "Work Order Item", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "required_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": "Required Items", + "length": 0, + "no_copy": 1, + "options": "Work Order Item", + "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": "time", - "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": "Time", - "length": 0, - "no_copy": 0, - "options": "fa fa-time", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "time", + "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": "Time", + "length": 0, + "no_copy": 0, + "options": "fa fa-time", + "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "now", - "fieldname": "planned_start_date", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Planned Start Date", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "now", + "fieldname": "planned_start_date", + "fieldtype": "Datetime", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Planned Start Date", + "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": 1, + "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": "actual_start_date", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Actual Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "actual_start_date", + "fieldtype": "Datetime", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Actual Start Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_13", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_13", + "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": "planned_end_date", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Planned End Date", - "length": 0, - "no_copy": 1, - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "planned_end_date", + "fieldtype": "Datetime", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Planned End Date", + "length": 0, + "no_copy": 1, + "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": "actual_end_date", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Actual End Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "actual_end_date", + "fieldtype": "Datetime", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Actual End Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "expected_delivery_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Expected Delivery Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fieldname": "expected_delivery_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Expected Delivery Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "operations_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": "Operations", - "length": 0, - "no_copy": 0, - "options": "fa fa-wrench", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fieldname": "operations_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": "Operations", + "length": 0, + "no_copy": 0, + "options": "fa fa-wrench", + "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": "Work Order", - "depends_on": "operations", - "fieldname": "transfer_material_against", - "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": "Transfer Material Against", - "length": 0, - "no_copy": 0, - "options": "\nWork Order\nJob Card", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Work Order", + "depends_on": "operations", + "fieldname": "transfer_material_against", + "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": "Transfer Material Against", + "length": 0, + "no_copy": 0, + "options": "\nWork Order\nJob Card", + "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": "operations", - "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": "Operations", - "length": 0, - "no_copy": 0, - "options": "Work Order Operation", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fieldname": "operations", + "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": "Operations", + "length": 0, + "no_copy": 0, + "options": "Work Order Operation", + "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, - "depends_on": "operations", - "fieldname": "section_break_22", - "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": "Operation Cost", - "length": 0, - "no_copy": 0, - "options": "", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "operations", + "fieldname": "section_break_22", + "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": "Operation Cost", + "length": 0, + "no_copy": 0, + "options": "", + "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": "planned_operating_cost", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Planned Operating Cost", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "planned_operating_cost", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Planned Operating Cost", + "length": 0, + "no_copy": 0, + "options": "Company:company:default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "actual_operating_cost", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Actual Operating Cost", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "actual_operating_cost", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Actual Operating Cost", + "length": 0, + "no_copy": 1, + "options": "Company:company:default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "additional_operating_cost", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Operating Cost", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "additional_operating_cost", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Additional Operating Cost", + "length": 0, + "no_copy": 1, + "options": "Company:company:default_currency", + "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": "column_break_24", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_24", + "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": "total_operating_cost", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Operating Cost", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "total_operating_cost", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Total Operating Cost", + "length": 0, + "no_copy": 1, + "options": "Company:company:default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "more_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "options": "fa fa-file-text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "more_info", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "More Information", + "length": 0, + "no_copy": 0, + "options": "fa fa-file-text", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Item Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "stock_uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Stock UOM", - "length": 0, - "no_copy": 0, - "oldfieldname": "stock_uom", - "oldfieldtype": "Data", - "options": "UOM", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fieldname": "stock_uom", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Stock UOM", + "length": 0, + "no_copy": 0, + "oldfieldname": "stock_uom", + "oldfieldtype": "Data", + "options": "UOM", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0, "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Manufacture against Material Request", - "fieldname": "material_request", - "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": "Material Request", - "length": 0, - "no_copy": 0, - "options": "Material Request", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "Manufacture against Material Request", + "fieldname": "material_request", + "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": "Material Request", + "length": 0, + "no_copy": 0, + "options": "Material Request", + "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": "material_request_item", - "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": "Material Request Item", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "material_request_item", + "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": "Material Request Item", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_order_item", - "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": "Sales Order Item", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sales_order_item", + "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": "Sales Order Item", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "production_plan", - "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": "Production Plan", - "length": 0, - "no_copy": 1, - "options": "Production Plan", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "production_plan", + "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": "Production Plan", + "length": 0, + "no_copy": 1, + "options": "Production Plan", + "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": "production_plan_item", - "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": "Production Plan Item", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "production_plan_item", + "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": "Production Plan Item", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "product_bundle_item", - "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": "Product Bundle Item", - "length": 0, - "no_copy": 1, - "options": "Item", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "product_bundle_item", + "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": "Product Bundle Item", + "length": 0, + "no_copy": 1, + "options": "Item", + "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": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Work Order", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "amended_from", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 1, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Amended From", + "length": 0, + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Work Order", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-cogs", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-12-13 15:33:12.490710", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Work Order", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "fa fa-cogs", + "idx": 1, + "image_view": 0, + "in_create": 0, + "is_submittable": 1, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2019-02-05 03:02:39.126868", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Work Order", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "set_user_permissions": 1, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "set_user_permissions": 1, + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 1, - "role": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 1, + "role": "Stock User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "ASC", - "title_field": "production_item", - "track_changes": 1, - "track_seen": 1, + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_order": "ASC", + "title_field": "production_item", + "track_changes": 1, + "track_seen": 1, "track_views": 0 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 947d6931e2..59a88d46da 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -568,11 +568,10 @@ def get_item_details(item, project = None): frappe.throw(_("Default BOM for {0} not found").format(item)) bom_data = frappe.db.get_value('BOM', res['bom_no'], - ['project', 'allow_alternative_item', 'transfer_material_against'], as_dict=1) + ['project', 'allow_alternative_item', 'transfer_material_against', 'item_name'], as_dict=1) - res['project'] = project or bom_data.project - res['allow_alternative_item'] = bom_data.allow_alternative_item - res['transfer_material_against'] = bom_data.transfer_material_against + res['project'] = project or bom_data.pop("project") + res.update(bom_data) res.update(check_if_scrap_warehouse_mandatory(res["bom_no"])) return res diff --git a/erpnext/patches.txt b/erpnext/patches.txt old mode 100644 new mode 100755 index b7e673da34..3785ec4fc7 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -586,3 +586,4 @@ erpnext.patches.v11_0.remove_barcodes_field_from_copy_fields_to_variants erpnext.patches.v12_0.set_task_status erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019 erpnext.patches.v11_0.make_italian_localization_fields # 01-03-2019 +erpnext.patches.v12_0.add_item_name_in_work_orders diff --git a/erpnext/patches/v12_0/add_item_name_in_work_orders.py b/erpnext/patches/v12_0/add_item_name_in_work_orders.py new file mode 100644 index 0000000000..35d526aaa3 --- /dev/null +++ b/erpnext/patches/v12_0/add_item_name_in_work_orders.py @@ -0,0 +1,13 @@ +import frappe + + +def execute(): + frappe.reload_doc("manufacturing", "doctype", "work_order") + + for wo in frappe.get_all("Work Order"): + item_code = frappe.db.get_value("Work Order", wo.name, "production_item") + item_name = frappe.db.get_value("Item", item_code, "item_name") + + frappe.db.set_value("Work Order", wo.name, "item_name", item_name, update_modified=False) + + frappe.db.commit() \ No newline at end of file From 6cbd3998fb143c48396841fa7c8fe8c3c4c4574b Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Mon, 18 Feb 2019 12:29:57 +0530 Subject: [PATCH 073/129] fix(work_order): Use single update query --- erpnext/patches.txt | 2 +- .../patches/v12_0/add_item_name_in_work_orders.py | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) mode change 100755 => 100644 erpnext/patches.txt diff --git a/erpnext/patches.txt b/erpnext/patches.txt old mode 100755 new mode 100644 index 3785ec4fc7..ee86816593 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -586,4 +586,4 @@ erpnext.patches.v11_0.remove_barcodes_field_from_copy_fields_to_variants erpnext.patches.v12_0.set_task_status erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019 erpnext.patches.v11_0.make_italian_localization_fields # 01-03-2019 -erpnext.patches.v12_0.add_item_name_in_work_orders +erpnext.patches.v12_0.add_item_name_in_work_orders \ No newline at end of file diff --git a/erpnext/patches/v12_0/add_item_name_in_work_orders.py b/erpnext/patches/v12_0/add_item_name_in_work_orders.py index 35d526aaa3..485dd314a1 100644 --- a/erpnext/patches/v12_0/add_item_name_in_work_orders.py +++ b/erpnext/patches/v12_0/add_item_name_in_work_orders.py @@ -4,10 +4,11 @@ import frappe def execute(): frappe.reload_doc("manufacturing", "doctype", "work_order") - for wo in frappe.get_all("Work Order"): - item_code = frappe.db.get_value("Work Order", wo.name, "production_item") - item_name = frappe.db.get_value("Item", item_code, "item_name") - - frappe.db.set_value("Work Order", wo.name, "item_name", item_name, update_modified=False) - + frappe.db.sql(""" + UPDATE + `tabWork Order` wo + JOIN `tabItem` item ON wo.production_item = item.item_code + SET + wo.item_name = item.item_name + """) frappe.db.commit() \ No newline at end of file From 5327d0253d7904419b8c21ec573c8c3875d3fb79 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Thu, 14 Feb 2019 13:20:50 +0530 Subject: [PATCH 074/129] feat(issue): Create tasks from issues --- erpnext/support/doctype/issue/issue.js | 9 ++++++++- erpnext/support/doctype/issue/issue.py | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) mode change 100644 => 100755 erpnext/support/doctype/issue/issue.js mode change 100644 => 100755 erpnext/support/doctype/issue/issue.py diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js old mode 100644 new mode 100755 index d0a9bf3808..05c9130378 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -9,6 +9,13 @@ frappe.ui.form.on("Issue", { frm.set_value("status", "Closed"); frm.save(); }); + + frm.add_custom_button(__("Task"), function () { + frappe.model.open_mapped_doc({ + method: "erpnext.support.doctype.issue.issue.make_task", + frm: frm + }); + }, __("Make")); } else { frm.add_custom_button(__("Reopen"), function() { frm.set_value("status", "Open"); @@ -37,7 +44,7 @@ frappe.ui.form.on("Issue", { if (!frm.timeline.wrapper.find('.btn-split-issue').length) { let split_issue = __("Split Issue") $(``) .appendTo(frm.timeline.wrapper.find('.comment-header .asset-details:not([data-communication-type="Comment"])')) if (!frm.timeline.wrapper.data("split-issue-event-attached")){ diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py old mode 100644 new mode 100755 index 0b5eb539c8..3e498c8269 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -7,11 +7,13 @@ import json from frappe import _ from frappe.model.document import Document +from frappe.model.mapper import get_mapped_doc from frappe.utils import now from frappe.utils.user import is_website_user sender_field = "raised_by" + class Issue(Document): def get_feed(self): return "{0}: {1}".format(_(self.status), self.subject) @@ -97,6 +99,7 @@ class Issue(Document): doc.save(ignore_permissions=True) return replicated_issue.name + def get_list_context(context=None): return { "title": _("Issues"), @@ -107,6 +110,7 @@ def get_list_context(context=None): 'no_breadcrumbs': True } + def get_issue_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by=None): from frappe.www.list import get_list user = frappe.session.user @@ -124,12 +128,14 @@ def get_issue_list(doctype, txt, filters, limit_start, limit_page_length=20, ord return get_list(doctype, txt, filters, limit_start, limit_page_length, ignore_permissions=ignore_permissions) + @frappe.whitelist() def set_status(name, status): st = frappe.get_doc("Issue", name) st.status = status st.save() + def auto_close_tickets(): """ auto close the replied support tickets after 7 days """ auto_close_after_days = frappe.db.get_value("Support Settings", "Support Settings", "close_issue_after_days") or 7 @@ -150,6 +156,7 @@ def set_multiple_status(names, status): for name in names: set_status(name, status) + def has_website_permission(doc, ptype, user, verbose=False): from erpnext.controllers.website_list_for_contact import has_website_permission permission_based_on_customer = has_website_permission(doc, ptype, user, verbose) @@ -160,3 +167,18 @@ def has_website_permission(doc, ptype, user, verbose=False): def update_issue(contact, method): """Called when Contact is deleted""" frappe.db.sql("""UPDATE `tabIssue` set contact='' where contact=%s""", contact.name) + + +@frappe.whitelist() +def make_task(source_name, target_doc=None): + def set_missing_values(source, target): + if not target.project: + target.project = frappe.db.get_value("Project", {"customer": source.customer}) + + doclist = get_mapped_doc("Issue", source_name, { + "Issue": { + "doctype": "Task" + } + }, target_doc, set_missing_values) + + return doclist From 2ba21fb66c8109a93c4170c2c828960da61903cb Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Thu, 14 Feb 2019 16:27:57 +0530 Subject: [PATCH 075/129] fix(issue): Add issue name in Task and fix description in Issue --- erpnext/projects/doctype/task/task.json | 126 +++++++++++++++-------- erpnext/support/doctype/issue/issue.json | 12 ++- erpnext/support/doctype/issue/issue.py | 38 ++++--- 3 files changed, 113 insertions(+), 63 deletions(-) diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index d904d7092b..2602aef626 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -79,6 +79,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "issue", + "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": "Issue", + "length": 0, + "no_copy": 0, + "options": "Issue", + "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, @@ -213,6 +246,38 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "color", + "fieldtype": "Color", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Color", + "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, @@ -251,11 +316,11 @@ "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", + "collapsible": 1, + "collapsible_depends_on": "eval:doc.__islocal", "columns": 0, "depends_on": "", - "fieldname": "section_break_10", + "fieldname": "sb_timeline", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -264,6 +329,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, + "label": "Timeline", "length": 0, "no_copy": 0, "permlevel": 0, @@ -513,38 +579,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "color", - "fieldtype": "Color", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Color", - "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, @@ -554,7 +588,7 @@ "collapsible_depends_on": "", "columns": 0, "depends_on": "", - "fieldname": "section_break0", + "fieldname": "sb_details", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -563,10 +597,11 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, + "label": "Details", "length": 0, "no_copy": 0, "oldfieldtype": "Section Break", - "options": "Simple", + "options": "", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -596,7 +631,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Details", + "label": "Task Description", "length": 0, "no_copy": 0, "oldfieldname": "description", @@ -624,7 +659,7 @@ "collapsible_depends_on": "", "columns": 0, "depends_on": "", - "fieldname": "section_break", + "fieldname": "sb_depends_on", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -726,7 +761,7 @@ "columns": 0, "depends_on": "", "description": "", - "fieldname": "actual", + "fieldname": "sb_actual", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -893,10 +928,10 @@ "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, - "collapsible": 0, + "collapsible": 1, "columns": 0, "depends_on": "", - "fieldname": "section_break_17", + "fieldname": "sb_costing", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -905,6 +940,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, + "label": "Costing", "length": 0, "no_copy": 0, "permlevel": 0, @@ -1058,9 +1094,9 @@ "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, - "collapsible": 0, + "collapsible": 1, "columns": 0, - "fieldname": "more_details", + "fieldname": "sb_more_info", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -1069,7 +1105,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "", + "label": "More Info", "length": 0, "no_copy": 0, "permlevel": 0, diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 21cf2f7848..7cb0df28a5 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -349,8 +350,9 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 1, + "collapsible_depends_on": "eval:doc.status!=\"Closed\"", "columns": 0, - "fieldname": "section_break_7", + "fieldname": "sb_details", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -416,7 +418,7 @@ "bold": 0, "collapsible": 1, "columns": 0, - "fieldname": "response", + "fieldname": "sb_response", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -511,7 +513,7 @@ "bold": 0, "collapsible": 1, "columns": 0, - "fieldname": "additional_info", + "fieldname": "sb_additional_info", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -736,7 +738,7 @@ "bold": 0, "collapsible": 1, "columns": 0, - "fieldname": "section_break_19", + "fieldname": "sb_resoution", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -1035,7 +1037,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-21 14:44:27.615004", + "modified": "2019-02-14 02:55:47.562611", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 3e498c8269..7e13947ee9 100755 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -19,10 +19,12 @@ class Issue(Document): return "{0}: {1}".format(_(self.status), self.subject) def validate(self): - if (self.get("__islocal") and self.via_customer_portal): + if self.is_new() and self.via_customer_portal: self.flags.create_communication = True + if not self.raised_by: self.raised_by = frappe.session.user + self.update_status() self.set_lead_contact(self.raised_by) @@ -32,16 +34,18 @@ class Issue(Document): def on_update(self): # create the communication email and remove the description - if (self.flags.create_communication and self.via_customer_portal): + if self.flags.create_communication and self.via_customer_portal: self.create_communication() self.flags.communication_created = None def set_lead_contact(self, email_id): import email.utils + email_id = email.utils.parseaddr(email_id)[1] if email_id: if not self.lead: self.lead = frappe.db.get_value("Lead", {"email_id": email_id}) + if not self.contact and not self.customer: self.contact = frappe.db.get_value("Contact", {"email_id": email_id}) @@ -81,22 +85,27 @@ class Issue(Document): communication.ignore_mandatory = True communication.save() - self.db_set("description", "") - def split_issue(self, subject, communication_id): # Bug: Pressing enter doesn't send subject from copy import deepcopy + replicated_issue = deepcopy(self) replicated_issue.subject = subject frappe.get_doc(replicated_issue).insert() + # Replicate linked Communications - # todo get all communications in timeline before this, and modify them to append them to new doc + # TODO: get all communications in timeline before this, and modify them to append them to new doc comm_to_split_from = frappe.get_doc("Communication", communication_id) - communications = frappe.get_all("Communication", filters={"reference_name": comm_to_split_from.reference_name, "reference_doctype": "Issue", "creation": ('>=', comm_to_split_from.creation)}) + communications = frappe.get_all("Communication", + filters={"reference_doctype": "Issue", + "reference_name": comm_to_split_from.reference_name, + "creation": ('>=', comm_to_split_from.creation)}) + for communication in communications: doc = frappe.get_doc("Communication", communication.name) doc.reference_name = replicated_issue.name doc.save(ignore_permissions=True) + return replicated_issue.name @@ -113,9 +122,11 @@ def get_list_context(context=None): def get_issue_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by=None): from frappe.www.list import get_list + user = frappe.session.user contact = frappe.db.get_value('Contact', {'user': user}, 'name') customer = None + if contact: contact_doc = frappe.get_doc('Contact', contact) customer = contact_doc.get_link_for('Customer') @@ -129,6 +140,13 @@ def get_issue_list(doctype, txt, filters, limit_start, limit_page_length=20, ord return get_list(doctype, txt, filters, limit_start, limit_page_length, ignore_permissions=ignore_permissions) +@frappe.whitelist() +def set_multiple_status(names, status): + names = json.loads(names) + for name in names: + set_status(name, status) + + @frappe.whitelist() def set_status(name, status): st = frappe.get_doc("Issue", name) @@ -137,7 +155,7 @@ def set_status(name, status): def auto_close_tickets(): - """ auto close the replied support tickets after 7 days """ + """Auto-close replied support tickets after 7 days""" auto_close_after_days = frappe.db.get_value("Support Settings", "Support Settings", "close_issue_after_days") or 7 issues = frappe.db.sql(""" select name from tabIssue where status='Replied' and @@ -150,12 +168,6 @@ def auto_close_tickets(): doc.flags.ignore_mandatory = True doc.save() -@frappe.whitelist() -def set_multiple_status(names, status): - names = json.loads(names) - for name in names: - set_status(name, status) - def has_website_permission(doc, ptype, user, verbose=False): from erpnext.controllers.website_list_for_contact import has_website_permission From fa77b591ace3929aeb373d725173fcc55a38cda2 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Thu, 14 Feb 2019 16:32:11 +0530 Subject: [PATCH 076/129] fix(issue): View Tasks against an Issue --- erpnext/support/doctype/issue/issue.js | 4 ++++ erpnext/support/doctype/issue/issue.py | 0 2 files changed, 4 insertions(+) mode change 100755 => 100644 erpnext/support/doctype/issue/issue.js mode change 100755 => 100644 erpnext/support/doctype/issue/issue.py diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js old mode 100755 new mode 100644 index 05c9130378..27bb46986b --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -4,6 +4,10 @@ frappe.ui.form.on("Issue", { }, refresh: function(frm) { + frm.add_custom_button(__("Task"), function () { + frappe.set_route("List", "Task", { "issue": frm.doc.name }); + }, __("View")); + if(frm.doc.status!=="Closed") { frm.add_custom_button(__("Close"), function() { frm.set_value("status", "Closed"); diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py old mode 100755 new mode 100644 From bf7c69f3c4f5b5c268e4ed6a762e0bba3703a40f Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Fri, 15 Feb 2019 14:21:10 +0530 Subject: [PATCH 077/129] fix(issue): Don't auto-set project --- erpnext/support/doctype/issue/issue.js | 8 ++++---- erpnext/support/doctype/issue/issue.py | 12 +++--------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 27bb46986b..03e1aa4f87 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -3,13 +3,13 @@ frappe.ui.form.on("Issue", { frm.email_field = "raised_by"; }, - refresh: function(frm) { + refresh: function (frm) { frm.add_custom_button(__("Task"), function () { frappe.set_route("List", "Task", { "issue": frm.doc.name }); }, __("View")); - if(frm.doc.status!=="Closed") { - frm.add_custom_button(__("Close"), function() { + if (frm.doc.status !== "Closed") { + frm.add_custom_button(__("Close"), function () { frm.set_value("status", "Closed"); frm.save(); }); @@ -21,7 +21,7 @@ frappe.ui.form.on("Issue", { }); }, __("Make")); } else { - frm.add_custom_button(__("Reopen"), function() { + frm.add_custom_button(__("Reopen"), function () { frm.set_value("status", "Open"); frm.save(); }); diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 7e13947ee9..de3d144a7e 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -33,7 +33,7 @@ class Issue(Document): clear(self.doctype, self.name) def on_update(self): - # create the communication email and remove the description + # Add a communication in the issue timeline if self.flags.create_communication and self.via_customer_portal: self.create_communication() self.flags.communication_created = None @@ -183,14 +183,8 @@ def update_issue(contact, method): @frappe.whitelist() def make_task(source_name, target_doc=None): - def set_missing_values(source, target): - if not target.project: - target.project = frappe.db.get_value("Project", {"customer": source.customer}) - - doclist = get_mapped_doc("Issue", source_name, { + return get_mapped_doc("Issue", source_name, { "Issue": { "doctype": "Task" } - }, target_doc, set_missing_values) - - return doclist + }, target_doc) From f4bce6a66e6701b05fed036ef5b5e4ee7295109d Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Mon, 18 Feb 2019 12:53:11 +0530 Subject: [PATCH 078/129] fix(issue): Replace Make buttons on Issue and Task with a dashboard --- erpnext/projects/doctype/task/task.js | 13 ------------- .../projects/doctype/task/task_dashboard.py | 19 +++++++++++++++++++ erpnext/support/doctype/issue/issue.js | 4 ---- .../support/doctype/issue/issue_dashboard.py | 15 +++++++++++++++ 4 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 erpnext/projects/doctype/task/task_dashboard.py create mode 100644 erpnext/support/doctype/issue/issue_dashboard.py diff --git a/erpnext/projects/doctype/task/task.js b/erpnext/projects/doctype/task/task.js index 93423db762..9a8af69426 100644 --- a/erpnext/projects/doctype/task/task.js +++ b/erpnext/projects/doctype/task/task.js @@ -35,19 +35,6 @@ frappe.ui.form.on("Task", { } if(!doc.__islocal) { - if(frappe.model.can_read("Timesheet")) { - frm.add_custom_button(__("Timesheet"), function() { - frappe.route_options = {"project": doc.project, "task": doc.name} - frappe.set_route("List", "Timesheet"); - }, __("View"), true); - } - if(frappe.model.can_read("Expense Claim")) { - frm.add_custom_button(__("Expense Claims"), function() { - frappe.route_options = {"project": doc.project, "task": doc.name} - frappe.set_route("List", "Expense Claim"); - }, __("View"), true); - } - if(frm.perm[0].write) { if(frm.doc.status!=="Completed" && frm.doc.status!=="Cancelled") { frm.add_custom_button(__("Completed"), function() { diff --git a/erpnext/projects/doctype/task/task_dashboard.py b/erpnext/projects/doctype/task/task_dashboard.py new file mode 100644 index 0000000000..b776b98f67 --- /dev/null +++ b/erpnext/projects/doctype/task/task_dashboard.py @@ -0,0 +1,19 @@ +from __future__ import unicode_literals + +from frappe import _ + + +def get_data(): + return { + 'fieldname': 'task', + 'transactions': [ + { + 'label': _('Activity'), + 'items': ['Timesheet'] + }, + { + 'label': _('Accounting'), + 'items': ['Expense Claim'] + } + ] + } diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 03e1aa4f87..ce75304e77 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -4,10 +4,6 @@ frappe.ui.form.on("Issue", { }, refresh: function (frm) { - frm.add_custom_button(__("Task"), function () { - frappe.set_route("List", "Task", { "issue": frm.doc.name }); - }, __("View")); - if (frm.doc.status !== "Closed") { frm.add_custom_button(__("Close"), function () { frm.set_value("status", "Closed"); diff --git a/erpnext/support/doctype/issue/issue_dashboard.py b/erpnext/support/doctype/issue/issue_dashboard.py new file mode 100644 index 0000000000..2ac7c81615 --- /dev/null +++ b/erpnext/support/doctype/issue/issue_dashboard.py @@ -0,0 +1,15 @@ +from __future__ import unicode_literals + +from frappe import _ + + +def get_data(): + return { + 'fieldname': 'issue', + 'transactions': [ + { + 'label': _('Activity'), + 'items': ['Task'] + } + ] + } From 641e412853e8259128b67409414342d054e7a08f Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 12 Mar 2019 13:12:31 +0530 Subject: [PATCH 079/129] fix: Removed raw query and used frappe.get_all --- erpnext/projects/report/billing_summary.py | 40 +++++----------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/erpnext/projects/report/billing_summary.py b/erpnext/projects/report/billing_summary.py index cbea5f5c40..214dcef8fd 100644 --- a/erpnext/projects/report/billing_summary.py +++ b/erpnext/projects/report/billing_summary.py @@ -7,7 +7,6 @@ import frappe from frappe import _ from frappe.utils import time_diff_in_hours - def get_columns(): return [ { @@ -53,7 +52,6 @@ def get_columns(): def get_data(filters): data = [] - record = get_records(filters) for entries in record: @@ -61,27 +59,20 @@ def get_data(filters): total_billable_hours = 0 total_amount = 0 entries_exists = False - timesheet_details = get_timesheet_details(filters, entries.name) - for activity in timesheet_details: - entries_exists = True - time_start = activity.from_time time_end = frappe.utils.add_to_date(activity.from_time, hours=activity.hours) - from_date = frappe.utils.get_datetime(filters.from_date) to_date = frappe.utils.get_datetime(filters.to_date) if time_start <= from_date and time_end <= to_date: total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(activity, time_end, from_date, total_hours, total_billable_hours, total_amount) - elif time_start >= from_date and time_end >= to_date: total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(activity, to_date, time_start, total_hours, total_billable_hours, total_amount) - elif time_start >= from_date and time_end <= to_date: total_hours, total_billable_hours, total_amount = get_billable_and_total_hours(activity, time_end, time_start, total_hours, total_billable_hours, total_amount) @@ -102,28 +93,15 @@ def get_data(filters): return data def get_records(filters): - if "employee" in filters: - return frappe.db.sql('''SELECT - employee, employee_name, name, total_billable_hours, total_hours, total_billable_amount - FROM - `tabTimesheet` - WHERE - employee = %s and (start_date <= %s and end_date >= %s)''', - (filters.employee, filters.to_date, filters.from_date), - as_dict=1 - ) + record_filters = [ + ["start_date", "<=", filters.to_date], + ["end_date", ">=", filters.from_date] + ] - elif "project" in filters: - return frappe.db.sql('''SELECT - employee, employee_name, name, total_billable_hours, total_hours, total_billable_amount - FROM - `tabTimesheet` - WHERE - start_date <= %s and end_date >= %s''',(filters.to_date, filters.from_date), - as_dict=1 - ) - else: - return {} + if "employee" in filters: + record_filters.append(["employee", "=", filters.employee]) + + return frappe.get_all("Timesheet", filters=record_filters, fields=[" * "] ) def get_billable_and_total_hours(activity, end, start, total_hours, total_billable_hours, total_amount): total_hours += abs(time_diff_in_hours(end, start)) @@ -142,4 +120,4 @@ def get_timesheet_details(filters, parent): "Timesheet Detail", filters = timesheet_details_filter, fields=["*"] - ) \ No newline at end of file + ) From 82433845e5cf34d16996a163afe622888c9bd4d4 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Thu, 14 Mar 2019 13:16:20 +0530 Subject: [PATCH 080/129] fix(transaction): Add link to payments made by Customer / Supplier in their dashboards --- erpnext/buying/doctype/supplier/supplier_dashboard.py | 11 ++++++++++- .../selling/doctype/customer/customer_dashboard.py | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/supplier/supplier_dashboard.py b/erpnext/buying/doctype/supplier/supplier_dashboard.py index aea1e2d38c..887a093736 100644 --- a/erpnext/buying/doctype/supplier/supplier_dashboard.py +++ b/erpnext/buying/doctype/supplier/supplier_dashboard.py @@ -1,11 +1,16 @@ from __future__ import unicode_literals + from frappe import _ + def get_data(): return { 'heatmap': True, 'heatmap_message': _('This is based on transactions against this Supplier. See timeline below for details'), 'fieldname': 'supplier', + 'non_standard_fieldnames': { + 'Payment Entry': 'party_name' + }, 'transactions': [ { 'label': _('Procurement'), @@ -15,9 +20,13 @@ def get_data(): 'label': _('Orders'), 'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] }, + { + 'label': _('Payments'), + 'items': ['Payment Entry'] + }, { 'label': _('Pricing'), 'items': ['Pricing Rule'] } ] - } \ No newline at end of file + } diff --git a/erpnext/selling/doctype/customer/customer_dashboard.py b/erpnext/selling/doctype/customer/customer_dashboard.py index f2f430a61e..3142ea22a4 100644 --- a/erpnext/selling/doctype/customer/customer_dashboard.py +++ b/erpnext/selling/doctype/customer/customer_dashboard.py @@ -1,11 +1,16 @@ from __future__ import unicode_literals + from frappe import _ + def get_data(): return { 'heatmap': True, 'heatmap_message': _('This is based on transactions against this Customer. See timeline below for details'), 'fieldname': 'customer', + 'non_standard_fieldnames': { + 'Payment Entry': 'party_name' + }, 'transactions': [ { 'label': _('Pre Sales'), @@ -15,6 +20,10 @@ def get_data(): 'label': _('Orders'), 'items': ['Sales Order', 'Delivery Note', 'Sales Invoice'] }, + { + 'label': _('Payments'), + 'items': ['Payment Entry'] + }, { 'label': _('Support'), 'items': ['Issue'] @@ -32,4 +41,4 @@ def get_data(): 'items': ['Subscription'] } ] - } \ No newline at end of file + } From e14758c89713c07ce35f15d6ff1a011aab95e999 Mon Sep 17 00:00:00 2001 From: Bassam Ramadan Date: Thu, 14 Mar 2019 11:31:50 +0200 Subject: [PATCH 081/129] fix: add returns field to cashier closing (#16911) --- .../cashier_closing/cashier_closing.json | 858 +++++++++--------- .../cashier_closing/cashier_closing.py | 4 +- 2 files changed, 435 insertions(+), 427 deletions(-) diff --git a/erpnext/accounts/doctype/cashier_closing/cashier_closing.json b/erpnext/accounts/doctype/cashier_closing/cashier_closing.json index 14e9070f30..115728dc7b 100644 --- a/erpnext/accounts/doctype/cashier_closing/cashier_closing.json +++ b/erpnext/accounts/doctype/cashier_closing/cashier_closing.json @@ -1,426 +1,434 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2018-06-18 16:51:49.994750", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Cashier-closing-", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Series", - "length": 0, - "no_copy": 0, - "options": "Cashier-closing-", - "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": "user", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "from_time", - "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "From Time", - "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": 1, - "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": "", - "fieldname": "time", - "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "To Time", - "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": 1, - "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": "0.00", - "fieldname": "expense", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Expense", - "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": "0.00", - "fieldname": "custody", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Custody", - "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": "0.00", - "fieldname": "outstanding_amount", - "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": "Outstanding Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0.0", - "fieldname": "payments", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payments", - "length": 0, - "no_copy": 0, - "options": "Cashier Closing Payments", - "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": "net_amount", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Net Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Cashier Closing", - "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 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-02-19 08:35:23.157327", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Cashier Closing", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "naming_series:", + "beta": 0, + "creation": "2018-06-18 16:51:49.994750", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "POS-CLO-", + "fieldname": "naming_series", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_global_search": 1, + "in_list_view": 0, + "in_standard_filter": 1, + "label": "Series", + "length": 0, + "no_copy": 0, + "options": "POS-CLO-", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "user", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "User", + "length": 0, + "no_copy": 0, + "options": "User", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Today", + "fieldname": "date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 1, + "label": "Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "from_time", + "fieldtype": "Time", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 1, + "label": "From Time", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "fieldname": "time", + "fieldtype": "Time", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 1, + "label": "To Time", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0.00", + "fieldname": "expense", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Expense", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "2", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0.00", + "fieldname": "custody", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Custody", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "2", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0.00", + "fieldname": "returns", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Returns", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "2", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0.00", + "fieldname": "outstanding_amount", + "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": "Outstanding Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "2", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0.0", + "fieldname": "payments", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Payments", + "length": 0, + "no_copy": 0, + "options": "Cashier Closing Payments", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "net_amount", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Net Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "amended_from", + "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": "Amended From", + "length": 0, + "no_copy": 1, + "options": "Cashier Closing", + "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, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 1, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2019-03-14 09:14:26.727129", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Cashier Closing", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 + } \ No newline at end of file diff --git a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py index 906bc7f18a..6de62ee577 100644 --- a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py +++ b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py @@ -29,8 +29,8 @@ class CashierClosing(Document): for i in self.payments: total += flt(i.amount) - self.net_amount = total + self.outstanding_amount + self.expense - self.custody + self.net_amount = total + self.outstanding_amount + self.expense - self.custody + self.returns def validate_time(self): if self.from_time >= self.time: - frappe.throw(_("From Time Should Be Less Than To Time")) + frappe.throw(_("From Time Should Be Less Than To Time")) \ No newline at end of file From 59a3012a5ce3b9e0229ed54d64f421da9cf60df7 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 15 Mar 2019 08:57:49 +0530 Subject: [PATCH 082/129] Update payment_request.py --- .../accounts/doctype/payment_request/payment_request.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 1fbde753d4..64c4124da6 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -214,11 +214,10 @@ class PaymentRequest(Document): def check_if_payment_entry_exists(self): if self.status == "Paid": - payment_entry = frappe.get_all("Payment Entry Reference", + if frappe.get_all("Payment Entry Reference", filters={"reference_name": self.reference_name, "docstatus": ["<", 2]}, - fields=["distinct(parent)"]) - - if any(payment_entry): + fields=["parent"], + limit=1): frappe.throw(_("Payment Entry already exists"), title=_('Error')) def make_communication_entry(self): From 08a209bc529358c0d641e189c6c1c59e74b67937 Mon Sep 17 00:00:00 2001 From: rushin29 Date: Fri, 15 Mar 2019 15:28:50 +0530 Subject: [PATCH 083/129] fix: gst_state_number for address with Unregistered GST (#16798) --- erpnext/regional/india/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index e7d0d5052e..9747b24569 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -9,6 +9,8 @@ from erpnext.hr.utils import get_salary_assignment from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip def validate_gstin_for_india(doc, method): + if hasattr(doc, 'gst_state') and doc.gst_state: + doc.gst_state_number = state_numbers[doc.gst_state] if not hasattr(doc, 'gstin') or not doc.gstin: return From b3434072367a214361e0932a9cdc4350a71fcd30 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sat, 16 Mar 2019 13:38:53 +0530 Subject: [PATCH 084/129] refactor: change validate email add to validate email address --- erpnext/crm/doctype/lead/lead.py | 4 ++-- erpnext/hr/doctype/employee/employee.py | 6 +++--- erpnext/hr/doctype/job_applicant/job_applicant.py | 4 ++-- erpnext/non_profit/doctype/member/member.py | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 29ca71bd88..442b6c2db2 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import (cstr, validate_email_add, cint, comma_and, has_gravatar, now, getdate, nowdate) +from frappe.utils import (cstr, validate_email_address, cint, comma_and, has_gravatar, now, getdate, nowdate) from frappe.model.mapper import get_mapped_doc from erpnext.controllers.selling_controller import SellingController @@ -38,7 +38,7 @@ class Lead(SellingController): if self.email_id: if not self.flags.ignore_email_validation: - validate_email_add(self.email_id, True) + validate_email_address(self.email_id, True) if self.email_id == self.lead_owner: frappe.throw(_("Lead Owner cannot be same as the Lead")) diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index d518cd8995..a403c393b9 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import getdate, validate_email_add, today, add_years, format_datetime +from frappe.utils import getdate, validate_email_address, today, add_years, format_datetime from frappe.model.naming import set_name_by_naming_series from frappe import throw, _, scrub from frappe.permissions import add_user_permission, remove_user_permission, \ @@ -142,9 +142,9 @@ class Employee(NestedSet): def validate_email(self): if self.company_email: - validate_email_add(self.company_email, True) + validate_email_address(self.company_email, True) if self.personal_email: - validate_email_add(self.personal_email, True) + validate_email_address(self.personal_email, True) def validate_status(self): if self.status == 'Left': diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.py b/erpnext/hr/doctype/job_applicant/job_applicant.py index ea81fe793d..4fc7719f38 100644 --- a/erpnext/hr/doctype/job_applicant/job_applicant.py +++ b/erpnext/hr/doctype/job_applicant/job_applicant.py @@ -7,7 +7,7 @@ from __future__ import unicode_literals from frappe.model.document import Document import frappe from frappe import _ -from frappe.utils import comma_and, validate_email_add +from frappe.utils import comma_and, validate_email_address sender_field = "email_id" @@ -28,7 +28,7 @@ class JobApplicant(Document): def validate(self): self.check_email_id_is_unique() if self.email_id: - validate_email_add(self.email_id, True) + validate_email_address(self.email_id, True) if not self.applicant_name and self.email_id: guess = self.email_id.split('@')[0] diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py index b9b2dd8fc9..9afaf90e7a 100644 --- a/erpnext/non_profit/doctype/member/member.py +++ b/erpnext/non_profit/doctype/member/member.py @@ -17,5 +17,5 @@ class Member(Document): self.validate_email_type(self.email) def validate_email_type(self, email): - from frappe.utils import validate_email_add - validate_email_add(email.strip(), True) \ No newline at end of file + from frappe.utils import validate_email_address + validate_email_address(email.strip(), True) \ No newline at end of file From 2ae7ed4cf0de848f1fbca04e278780f14a9daaf6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sun, 17 Mar 2019 09:49:24 +0530 Subject: [PATCH 085/129] fix: Gross profit report fix (#16935) --- erpnext/accounts/report/gross_profit/gross_profit.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 073516fb88..e5aaafaff4 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -125,10 +125,11 @@ class GrossProfitGenerator(object): # get buying amount if row.item_code in product_bundles: - row.buying_amount = self.get_buying_amount_from_product_bundle(row, - product_bundles[row.item_code]) + row.buying_amount = flt(self.get_buying_amount_from_product_bundle(row, + product_bundles[row.item_code]), self.currency_precision) else: - row.buying_amount = self.get_buying_amount(row, row.item_code) + row.buying_amount = flt(self.get_buying_amount(row, row.item_code), + self.currency_precision) # get buying rate if row.qty: @@ -215,7 +216,7 @@ class GrossProfitGenerator(object): if packed_item.get("parent_detail_docname")==row.item_row: buying_amount += self.get_buying_amount(row, packed_item.item_code) - return buying_amount + return flt(buying_amount, self.currency_precision) def get_buying_amount(self, row, item_code): # IMP NOTE From 3ead70ba3c4304f8f1248a91c128e18cf8996170 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 18 Mar 2019 08:22:57 +0530 Subject: [PATCH 086/129] fix: Change IBAN Account length from 25 to 30 (#16847) --- erpnext/accounts/doctype/bank_account/bank_account.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json index 4897097b4a..84a8e6857c 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.json +++ b/erpnext/accounts/doctype/bank_account/bank_account.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 1, @@ -290,7 +291,7 @@ "in_list_view": 1, "in_standard_filter": 0, "label": "IBAN", - "length": 25, + "length": 30, "no_copy": 0, "permlevel": 0, "precision": "", @@ -669,7 +670,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-07-20 13:55:36.996465", + "modified": "2019-03-05 17:56:05.103238", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Account", From ace95d5a67915ea0b621f59d834750a9d450609f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 18 Mar 2019 11:53:04 +0530 Subject: [PATCH 087/129] feat: added cost center filter in AR/AP reports --- .../doctype/purchase_invoice/purchase_invoice.js | 9 +++++++++ .../doctype/sales_invoice/sales_invoice.js | 9 +++++++++ .../report/accounts_payable/accounts_payable.js | 14 ++++++++++++++ .../accounts_payable_summary.js | 14 ++++++++++++++ .../accounts_receivable/accounts_receivable.js | 14 ++++++++++++++ .../accounts_receivable/accounts_receivable.py | 7 +++++++ .../accounts_receivable_summary.js | 14 ++++++++++++++ erpnext/selling/doctype/customer/customer.py | 15 ++++++++++++--- .../customer_credit_balance.js | 16 +++++++++++++++- .../customer_credit_balance.py | 5 +++-- 10 files changed, 111 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 5c0e8fa74a..ac2ce8e6d8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -510,6 +510,15 @@ frappe.ui.form.on("Purchase Invoice", { } } } + + frm.set_query("cost_center", function() { + return { + filters: { + company: frm.doc.company, + is_group: 0 + } + }; + }); }, onload: function(frm) { diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index a4a5940fc7..9a40d2b17e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -564,6 +564,15 @@ frappe.ui.form.on('Sales Invoice', { }; }); + frm.set_query("cost_center", function() { + return { + filters: { + company: frm.doc.company, + is_group: 0 + } + }; + }); + frm.custom_make_buttons = { 'Delivery Note': 'Delivery', 'Sales Invoice': 'Sales Return', diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 0a025f68d5..9dd552f3b8 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -50,6 +50,20 @@ frappe.query_reports["Accounts Payable"] = { "fieldtype": "Link", "options": "Finance Book" }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center", + get_query: () => { + var company = frappe.query_report.get_filter_value('company'); + return { + filters: { + 'company': company + } + } + } + }, { "fieldname":"supplier", "label": __("Supplier"), diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js index 7823cac89c..31c0193f33 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -50,6 +50,20 @@ frappe.query_reports["Accounts Payable Summary"] = { "fieldtype": "Link", "options": "Finance Book" }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center", + get_query: () => { + var company = frappe.query_report.get_filter_value('company'); + return { + filters: { + 'company': company + } + } + } + }, { "fieldname":"supplier", "label": __("Supplier"), diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index bbfee1112f..dce7e75578 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -50,6 +50,20 @@ frappe.query_reports["Accounts Receivable"] = { "fieldtype": "Link", "options": "Finance Book" }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center", + get_query: () => { + var company = frappe.query_report.get_filter_value('company'); + return { + filters: { + 'company': company + } + } + } + }, { "fieldname":"customer", "label": __("Customer"), diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 0e3317dcaa..4932ae1327 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -537,6 +537,13 @@ class ReceivablePayableReport(object): where supplier_group=%s)""") values.append(self.filters.get("supplier_group")) + if self.filters.get("cost_center"): + lft, rgt = frappe.get_cached_value("Cost Center", + self.filters.get("cost_center"), ['lft', 'rgt']) + + conditions.append("""cost_center in (select name from `tabCost Center` where + lft >= {0} and rgt <= {1})""".format(lft, rgt)) + accounts = [d.name for d in frappe.get_all("Account", filters={"account_type": account_type, "company": self.filters.company})] conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts))) diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js index a6f1457954..47b087db8b 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -50,6 +50,20 @@ frappe.query_reports["Accounts Receivable Summary"] = { "fieldtype": "Link", "options": "Finance Book" }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center", + get_query: () => { + var company = frappe.query_report.get_filter_value('company'); + return { + filters: { + 'company': company + } + } + } + }, { "fieldname":"customer", "label": __("Customer"), diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index a8fae7b5a9..ec27498cc7 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -260,12 +260,21 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, throw(_("Please contact to the user who have Sales Master Manager {0} role") .format(" / " + credit_controller if credit_controller else "")) -def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False): +def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None): # Outstanding based on GL Entries + + cond = "" + if cost_center: + lft, rgt = frappe.get_cached_value("Cost Center", + cost_center, ['lft', 'rgt']) + + cond = """ and cost_center in (select name from `tabCost Center` where + lft >= {0} and rgt <= {1})""".format(lft, rgt) + outstanding_based_on_gle = frappe.db.sql(""" select sum(debit) - sum(credit) - from `tabGL Entry` - where party_type = 'Customer' and party = %s and company=%s""", (customer, company)) + from `tabGL Entry` where party_type = 'Customer' + and party = %s and company=%s {0}""".format(cond), (customer, company), debug=1) outstanding_based_on_gle = flt(outstanding_based_on_gle[0][0]) if outstanding_based_on_gle else 0 diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.js b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.js index 3a99eb0891..de8abdc498 100644 --- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.js +++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.js @@ -16,6 +16,20 @@ frappe.query_reports["Customer Credit Balance"] = { "label": __("Customer"), "fieldtype": "Link", "options": "Customer" - } + }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center", + get_query: () => { + var company = frappe.query_report.get_filter_value('company'); + return { + filters: { + 'company': company + } + } + } + }, ] } diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py index fe58af61c8..a57d975740 100644 --- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py +++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py @@ -21,9 +21,10 @@ def execute(filters=None): row = [] outstanding_amt = get_customer_outstanding(d.name, filters.get("company"), - ignore_outstanding_sales_order=d.bypass_credit_limit_check_at_sales_order) + ignore_outstanding_sales_order=d.bypass_credit_limit_check_at_sales_order, + cost_center=filters.get("cost_center")) - credit_limit = get_credit_limit(d.name, filters.get("company")) + credit_limit = get_credit_limit(d.name, filters.get("company")) bal = flt(credit_limit) - flt(outstanding_amt) From aa03ea2a56a9a5d513ba35b43629a4cf04861f07 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Mon, 18 Mar 2019 12:36:42 +0530 Subject: [PATCH 088/129] disbale pl check for Sales and Purchase Invoice --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 810b6f79b5..5c76799e4e 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -74,7 +74,8 @@ class GLEntry(Document): def check_pl_account(self): if self.is_opening=='Yes' and \ - frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss": + frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss" and \ + self.voucher_type not in ['Purchase Invoice', 'Sales Invoice']: frappe.throw(_("{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry") .format(self.voucher_type, self.voucher_no, self.account)) From 1a4bfdd090c0c5f73bde90f81d3783d2b7d0a4c7 Mon Sep 17 00:00:00 2001 From: Zlash65 Date: Mon, 28 Jan 2019 16:35:41 +0530 Subject: [PATCH 089/129] feat: selecting parent_company should disable chart_of_accounts based fields --- erpnext/setup/doctype/company/company.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 70e047a4e8..dbfbc5932e 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -16,6 +16,14 @@ frappe.ui.form.on("Company", { filters: {"is_additional_component": 1} } }); + + frm.set_df_property("create_chart_of_accounts_based_on", "read_only", frm.doc.parent_company ? 1 : 0); + frm.set_df_property("existing_company", "read_only", frm.doc.parent_company ? 1 : 0); + frm.set_query("parent_company", function() { + return { + filters: {"is_group": 1} + } + }); }, company_name: function(frm) { @@ -28,6 +36,12 @@ frappe.ui.form.on("Company", { } }, + parent_company: function(frm) { + if(!frm.doc.parent_company) return; + frm.set_value("create_chart_of_accounts_based_on", "Existing Company"); + frm.set_value("existing_company", frm.doc.parent_company); + }, + date_of_commencement: function(frm) { if(frm.doc.date_of_commencement Date: Mon, 28 Jan 2019 16:39:14 +0530 Subject: [PATCH 090/129] feat: once company is saved, parent_company cannot be selected --- erpnext/setup/doctype/company/company.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index dbfbc5932e..5592e9bd64 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -53,8 +53,9 @@ frappe.ui.form.on("Company", { }, refresh: function(frm) { - if(frm.doc.abbr && !frm.doc.__islocal) { + if(!frm.doc.__islocal) { frm.set_df_property("abbr", "read_only", 1); + frm.set_df_property("parent_company", "read_only", 1); } frm.toggle_display('address_html', !frm.doc.__islocal); From 2def228da88043f944053826d4e7a92f15895c6d Mon Sep 17 00:00:00 2001 From: Zlash65 Date: Tue, 29 Jan 2019 12:28:56 +0530 Subject: [PATCH 091/129] feat: allow adding account only if topmost parent company - hidden filter for Parent Company added - Add button overriden with new condition --- .../accounts/doctype/account/account_tree.js | 58 ++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index a9cbdd5dee..5a80258e71 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -4,13 +4,40 @@ frappe.treeview_settings["Account"] = { breadcrumbs: "Accounts", title: __("Chart Of Accounts"), get_tree_root: false, - filters: [{ - fieldname: "company", - fieldtype:"Select", - options: erpnext.utils.get_tree_options("company"), - label: __("Company"), - default: erpnext.utils.get_tree_default("company") - }], + filters: [ + { + fieldname: "company", + fieldtype:"Select", + options: erpnext.utils.get_tree_options("company"), + label: __("Company"), + default: erpnext.utils.get_tree_default("company"), + on_change: function() { + var me = frappe.treeview_settings['Account'].treeview; + var company = me.page.fields_dict.company.get_value(); + frappe.call({ + method: "frappe.client.get_value", + args: { + doctype: "Company", + fieldname: "parent_company", + filters: { name: company}, + }, + callback: function(r, rt) { + if(r.message) { + me.page.fields_dict.parent_company.set_value(r.message["parent_company"] || ""); + } + } + }); + } + }, + { + fieldname: "parent_company", + fieldtype:"Data", + fetch_from: "company.parent_company", + label: __("Parent Company"), + hidden: true, + disable_onchange: true + } + ], root_label: "Accounts", get_tree_nodes: 'erpnext.accounts.utils.get_children', add_tree_node: 'erpnext.accounts.utils.add_ac', @@ -42,8 +69,8 @@ frappe.treeview_settings["Account"] = { ], ignore_fields:["parent_account"], onload: function(treeview) { - frappe.treeview_settings['Account'].page = {}; - $.extend(frappe.treeview_settings['Account'].page, treeview.page); + frappe.treeview_settings['Account'].treeview = {}; + $.extend(frappe.treeview_settings['Account'].treeview, treeview); function get_company() { return treeview.page.fields_dict.company.get_value(); } @@ -93,6 +120,19 @@ frappe.treeview_settings["Account"] = { } }, toolbar: [ + { + label:__("Add Child"), + condition: function(node) { + return frappe.boot.user.can_create.indexOf("Account") !== -1 && + !frappe.treeview_settings['Account'].treeview.page.fields_dict.parent_company.get_value() && + node.expandable && !node.hide_add; + }, + click: function(node) { + var me = frappe.treeview_settings['Account'].treeview; + me.new_node(node); + }, + btnClass: "hidden-xs" + }, { condition: function(node) { return !node.root && frappe.boot.user.can_read.indexOf("GL Entry") !== -1 From 0407bf1e005a316322118bcf7043b834183fb788 Mon Sep 17 00:00:00 2001 From: Zlash65 Date: Wed, 30 Jan 2019 11:12:15 +0530 Subject: [PATCH 092/129] fix: override primary action button, change filter to root company --- .../accounts/doctype/account/account_tree.js | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index 5a80258e71..4bedb3c26c 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -15,25 +15,23 @@ frappe.treeview_settings["Account"] = { var me = frappe.treeview_settings['Account'].treeview; var company = me.page.fields_dict.company.get_value(); frappe.call({ - method: "frappe.client.get_value", + method: "erpnext.accounts.doctype.account.account.get_root_company", args: { - doctype: "Company", - fieldname: "parent_company", - filters: { name: company}, + company: company, }, callback: function(r, rt) { if(r.message) { - me.page.fields_dict.parent_company.set_value(r.message["parent_company"] || ""); + let root_company = r.message.length ? r.message[0] : ""; + me.page.fields_dict.root_company.set_value(root_company); } } }); } }, { - fieldname: "parent_company", + fieldname: "root_company", fieldtype:"Data", - fetch_from: "company.parent_company", - label: __("Parent Company"), + label: __("Root Company"), hidden: true, disable_onchange: true } @@ -105,6 +103,18 @@ frappe.treeview_settings["Account"] = { } }, + post_render: function(treeview) { + frappe.treeview_settings['Account'].treeview["tree"] = treeview.tree; + treeview.page.set_primary_action(__("New"), function() { + let root_company = treeview.page.fields_dict.root_company.get_value(); + + if(root_company) { + frappe.throw(__("Please add the account to root level Company - ") + root_company); + } else { + treeview.new_node(); + } + }, "octicon octicon-plus"); + }, onrender: function(node) { if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){ var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr"; @@ -124,12 +134,12 @@ frappe.treeview_settings["Account"] = { label:__("Add Child"), condition: function(node) { return frappe.boot.user.can_create.indexOf("Account") !== -1 && - !frappe.treeview_settings['Account'].treeview.page.fields_dict.parent_company.get_value() && + !frappe.treeview_settings['Account'].treeview.page.fields_dict.root_company.get_value() && node.expandable && !node.hide_add; }, click: function(node) { var me = frappe.treeview_settings['Account'].treeview; - me.new_node(node); + me.new_node(); }, btnClass: "hidden-xs" }, @@ -143,7 +153,7 @@ frappe.treeview_settings["Account"] = { "account": node.label, "from_date": frappe.sys_defaults.year_start_date, "to_date": frappe.sys_defaults.year_end_date, - "company": frappe.treeview_settings['Account'].page.fields_dict.company.get_value() + "company": frappe.treeview_settings['Account'].treeview.page.fields_dict.company.get_value() }; frappe.set_route("query-report", "General Ledger"); }, From 88c990901d5a060a7d2e9c4db27c30d95b064d8f Mon Sep 17 00:00:00 2001 From: Zlash65 Date: Wed, 30 Jan 2019 11:12:28 +0530 Subject: [PATCH 093/129] fix: added validation to if account is being added to child company --- erpnext/accounts/doctype/account/account.py | 14 ++++++++++++++ erpnext/setup/doctype/company/company.py | 1 + 2 files changed, 15 insertions(+) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 5d504b94be..d699146049 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -34,6 +34,7 @@ class Account(NestedSet): return self.validate_parent() self.validate_root_details() + self.validate_root_company() validate_field_number("Account", self.name, self.account_number, self.company, "account_number") self.validate_group_or_ledger() self.set_root_and_report_type() @@ -90,6 +91,13 @@ class Account(NestedSet): if not self.parent_account and not self.is_group: frappe.throw(_("Root Account must be a group")) + def validate_root_company(self): + # fetch all ancestors in top-down hierarchy + if frappe.local.flags.ignore_root_company_validation: return + ancestors = get_root_company(self.company) + if ancestors: + frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0])) + def validate_group_or_ledger(self): if self.get("__islocal"): return @@ -250,3 +258,9 @@ def merge_account(old, new, is_group, root_type, company): frappe.rename_doc("Account", old, new, merge=1, ignore_permissions=1) return new + +@frappe.whitelist() +def get_root_company(company): + # return the topmost company in the hierarchy + ancestors = frappe.utils.nestedset.get_ancestors_of('Company', company, "lft asc") + return [ancestors[0]] if ancestors else [] diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index c49c264251..33361e81d4 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -141,6 +141,7 @@ class Company(NestedSet): def create_default_accounts(self): from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts + frappe.local.flags.ignore_root_company_validation = True create_charts(self.name, self.chart_of_accounts, self.existing_company) frappe.db.set(self, "default_receivable_account", frappe.db.get_value("Account", From d787ef8b8e504a499c5fda283861a3f17910f781 Mon Sep 17 00:00:00 2001 From: Zlash65 Date: Wed, 30 Jan 2019 14:00:56 +0530 Subject: [PATCH 094/129] feat: sync account created for a group company to all its descendants --- erpnext/accounts/doctype/account/account.py | 30 ++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index d699146049..dea82d8ed6 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import cint, cstr from frappe import throw, _ -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_ancestors_of, get_descendants_of class RootNotEditable(frappe.ValidationError): pass class BalanceMismatchError(frappe.ValidationError): pass @@ -34,7 +34,6 @@ class Account(NestedSet): return self.validate_parent() self.validate_root_details() - self.validate_root_company() validate_field_number("Account", self.name, self.account_number, self.company, "account_number") self.validate_group_or_ledger() self.set_root_and_report_type() @@ -42,6 +41,7 @@ class Account(NestedSet): self.validate_frozen_accounts_modifier() self.validate_balance_must_be_debit_or_credit() self.validate_account_currency() + self.validate_root_company_and_sync_account_to_children() def validate_parent(self): """Fetch Parent Details and validate parent account""" @@ -91,12 +91,30 @@ class Account(NestedSet): if not self.parent_account and not self.is_group: frappe.throw(_("Root Account must be a group")) - def validate_root_company(self): - # fetch all ancestors in top-down hierarchy - if frappe.local.flags.ignore_root_company_validation: return + def validate_root_company_and_sync_account_to_children(self): + # ignore validation while creating new compnay or while syncing to child companies + if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation: + return + ancestors = get_root_company(self.company) if ancestors: frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0])) + else: + descendants = get_descendants_of('Company', self.company) + acc_name = frappe.db.get_value('Account', self.parent_account, "account_name") + + acc_name_map = {} + for d in frappe.db.get_values('Account', + {"company": ["in", descendants], "account_name": acc_name}, + ["company", "name"], as_dict=True): + acc_name_map[d["company"]] = d["name"] + + for company in descendants: + doc = frappe.copy_doc(self) + doc.flags.ignore_root_company_validation = True + doc.update({"company": company, "account_currency": None, + "parent": acc_name_map[company], "parent_account": acc_name_map[company]}) + doc.save() def validate_group_or_ledger(self): if self.get("__islocal"): @@ -262,5 +280,5 @@ def merge_account(old, new, is_group, root_type, company): @frappe.whitelist() def get_root_company(company): # return the topmost company in the hierarchy - ancestors = frappe.utils.nestedset.get_ancestors_of('Company', company, "lft asc") + ancestors = get_ancestors_of('Company', company, "lft asc") return [ancestors[0]] if ancestors else [] From 73acb8c83702e636a4b126add7a2181ddad2a012 Mon Sep 17 00:00:00 2001 From: Zlash65 Date: Wed, 13 Feb 2019 16:31:37 +0530 Subject: [PATCH 095/129] fix: set chart of accounts based on parent company on server side --- erpnext/setup/doctype/company/company.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 33361e81d4..ad9d64baf5 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -39,6 +39,7 @@ class Company(NestedSet): self.validate_coa_input() self.validate_perpetual_inventory() self.check_country_change() + self.set_chart_of_accounts() def validate_abbr(self): if not self.abbr: @@ -174,6 +175,12 @@ class Company(NestedSet): self.country != frappe.get_cached_value('Company', self.name, 'country'): frappe.flags.country_change = True + def set_chart_of_accounts(self): + ''' If parent company is set, chart of accounts will be based on that company ''' + if self.parent_company: + self.create_chart_of_accounts_based_on = "Existing Company" + self.existing_company = self.parent_company + def set_default_accounts(self): self._set_default_account("default_cash_account", "Cash") self._set_default_account("default_bank_account", "Bank") From f71cb8dc6db03ee5ddc50aac58ef52b4fc783c71 Mon Sep 17 00:00:00 2001 From: Zlash65 Date: Wed, 13 Feb 2019 16:32:04 +0530 Subject: [PATCH 096/129] fix: return if no descendants found --- erpnext/accounts/doctype/account/account.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index dea82d8ed6..e241fc4a3a 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -101,9 +101,10 @@ class Account(NestedSet): frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0])) else: descendants = get_descendants_of('Company', self.company) - acc_name = frappe.db.get_value('Account', self.parent_account, "account_name") + if not descendants: return acc_name_map = {} + acc_name = frappe.db.get_value('Account', self.parent_account, "account_name") for d in frappe.db.get_values('Account', {"company": ["in", descendants], "account_name": acc_name}, ["company", "name"], as_dict=True): From cc65447e62d768354984bb042e6f3a01043fc031 Mon Sep 17 00:00:00 2001 From: Zlash65 Date: Wed, 13 Feb 2019 16:32:50 +0530 Subject: [PATCH 097/129] fix: chart of accounts field toggling improv --- erpnext/setup/doctype/company/company.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 5592e9bd64..aff4baf931 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -17,8 +17,6 @@ frappe.ui.form.on("Company", { } }); - frm.set_df_property("create_chart_of_accounts_based_on", "read_only", frm.doc.parent_company ? 1 : 0); - frm.set_df_property("existing_company", "read_only", frm.doc.parent_company ? 1 : 0); frm.set_query("parent_company", function() { return { filters: {"is_group": 1} @@ -37,9 +35,10 @@ frappe.ui.form.on("Company", { }, parent_company: function(frm) { - if(!frm.doc.parent_company) return; - frm.set_value("create_chart_of_accounts_based_on", "Existing Company"); - frm.set_value("existing_company", frm.doc.parent_company); + var bool = frm.doc.parent_company ? true : false; + frm.set_value('create_chart_of_accounts_based_on', bool ? "Existing Company" : ""); + frm.set_value('existing_company', bool ? frm.doc.parent_company : ""); + disbale_coa_fields(frm, bool); }, date_of_commencement: function(frm) { @@ -54,8 +53,9 @@ frappe.ui.form.on("Company", { refresh: function(frm) { if(!frm.doc.__islocal) { - frm.set_df_property("abbr", "read_only", 1); + frm.doc.abbr && frm.set_df_property("abbr", "read_only", 1); frm.set_df_property("parent_company", "read_only", 1); + disbale_coa_fields(frm); } frm.toggle_display('address_html', !frm.doc.__islocal); @@ -271,3 +271,9 @@ erpnext.company.set_custom_query = function(frm, v) { } }); } + +var disbale_coa_fields = function(frm, bool=true) { + frm.set_df_property("create_chart_of_accounts_based_on", "read_only", bool); + frm.set_df_property("chart_of_accounts", "read_only", bool); + frm.set_df_property("existing_company", "read_only", bool); +}; \ No newline at end of file From 11bba571afb7a6bd49e5ac6bed623bdc8149e7be Mon Sep 17 00:00:00 2001 From: Zlash65 Date: Wed, 13 Feb 2019 16:33:28 +0530 Subject: [PATCH 098/129] test case added to check account syncing --- .../accounts/doctype/account/test_account.py | 13 +++++++ .../setup/doctype/company/test_records.json | 34 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index acaa0966a2..4c057d9549 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -97,6 +97,19 @@ class TestAccount(unittest.TestCase): self.assertRaises(frappe.ValidationError, merge_account, "Capital Stock - _TC",\ "Softwares - _TC", doc.is_group, doc.root_type, doc.company) + def test_account_sync(self): + del frappe.local.flags["ignore_root_company_validation"] + acc = frappe.new_doc("Account") + acc.account_name = "Test Sync Account" + acc.parent_account = "Temporary Accounts - _TC3" + acc.company = "_Test Company 3" + acc.insert() + + acc_tc_4 = frappe.db.get_value('Account', {'account_name': "Test Sync Account", "company": "_Test Company 4"}) + acc_tc_5 = frappe.db.get_value('Account', {'account_name': "Test Sync Account", "company": "_Test Company 5"}) + self.assertEqual(acc_tc_4, "Test Sync Account - _TC4") + self.assertEqual(acc_tc_5, "Test Sync Account - _TC5") + def _make_test_records(verbose): from frappe.test_runner import make_test_objects diff --git a/erpnext/setup/doctype/company/test_records.json b/erpnext/setup/doctype/company/test_records.json index 7e26ca3207..bf9d4bdd19 100644 --- a/erpnext/setup/doctype/company/test_records.json +++ b/erpnext/setup/doctype/company/test_records.json @@ -28,5 +28,39 @@ "domain": "Retail", "chart_of_accounts": "Standard", "default_holiday_list": "_Test Holiday List" + }, + { + "abbr": "_TC3", + "company_name": "_Test Company 3", + "is_group": 1, + "country": "India", + "default_currency": "INR", + "doctype": "Company", + "domain": "Manufacturing", + "chart_of_accounts": "Standard", + "default_holiday_list": "_Test Holiday List" + }, + { + "abbr": "_TC4", + "company_name": "_Test Company 4", + "parent_company": "_Test Company 3", + "is_group": 1, + "country": "India", + "default_currency": "INR", + "doctype": "Company", + "domain": "Manufacturing", + "chart_of_accounts": "Standard", + "default_holiday_list": "_Test Holiday List" + }, + { + "abbr": "_TC5", + "company_name": "_Test Company 5", + "parent_company": "_Test Company 4", + "country": "India", + "default_currency": "INR", + "doctype": "Company", + "domain": "Manufacturing", + "chart_of_accounts": "Standard", + "default_holiday_list": "_Test Holiday List" } ] From 01086ff60c54a5463f23f747c2681ac45a531ff9 Mon Sep 17 00:00:00 2001 From: Zlash65 Date: Wed, 13 Feb 2019 16:33:42 +0530 Subject: [PATCH 099/129] codacy fixes --- erpnext/accounts/doctype/account/account_tree.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index 4bedb3c26c..df0486cc27 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -19,7 +19,7 @@ frappe.treeview_settings["Account"] = { args: { company: company, }, - callback: function(r, rt) { + callback: function(r) { if(r.message) { let root_company = r.message.length ? r.message[0] : ""; me.page.fields_dict.root_company.set_value(root_company); @@ -137,7 +137,7 @@ frappe.treeview_settings["Account"] = { !frappe.treeview_settings['Account'].treeview.page.fields_dict.root_company.get_value() && node.expandable && !node.hide_add; }, - click: function(node) { + click: function() { var me = frappe.treeview_settings['Account'].treeview; me.new_node(); }, From d4e4316d0b9432a0b4ed36a54cdd97bc3a49d2fc Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 18 Mar 2019 10:53:43 +0530 Subject: [PATCH 100/129] fix: test cases and codacy --- erpnext/accounts/doctype/account/account.py | 2 + .../hr/doctype/staffing_plan/staffing_plan.py | 10 ++ .../staffing_plan/test_staffing_plan.py | 10 +- .../setup/doctype/company/test_records.json | 128 +++++++++--------- 4 files changed, 81 insertions(+), 69 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index e241fc4a3a..427f3dba20 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -116,6 +116,8 @@ class Account(NestedSet): doc.update({"company": company, "account_currency": None, "parent": acc_name_map[company], "parent_account": acc_name_map[company]}) doc.save() + frappe.msgprint(_("Account {0} is added in the child company {1}") + .format(doc.name, company)) def validate_group_or_ledger(self): if self.get("__islocal"): diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py index 70e185cde5..83e53135ef 100644 --- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py +++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py @@ -20,6 +20,7 @@ class StaffingPlan(Document): self.total_estimated_budget = 0 for detail in self.get("staffing_details"): + self.set_vacancies(detail) self.validate_overlap(detail) self.validate_with_subsidiary_plans(detail) self.validate_with_parent_plan(detail) @@ -39,6 +40,15 @@ class StaffingPlan(Document): else: detail.vacancies = detail.number_of_positions = detail.total_estimated_cost = 0 self.total_estimated_budget += detail.total_estimated_cost + def set_vacancies(self, row): + if not row.vacancies: + current_openings = 0 + for field in ['current_count', 'current_openings']: + if row.get(field): + current_openings += row.get(field) + + row.vacancies = row.number_of_positions - current_openings + def validate_overlap(self, staffing_plan_detail): # Validate if any submitted Staffing Plan exist for any Designations in this plan # and spd.vacancies>0 ? diff --git a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py index 66d9cdd07a..22dba99af0 100644 --- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py +++ b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py @@ -18,7 +18,7 @@ class TestStaffingPlan(unittest.TestCase): if frappe.db.exists("Staffing Plan", "Test"): return staffing_plan = frappe.new_doc("Staffing Plan") - staffing_plan.company = "_Test Company 3" + staffing_plan.company = "_Test Company 10" staffing_plan.name = "Test" staffing_plan.from_date = nowdate() staffing_plan.to_date = add_days(nowdate(), 10) @@ -67,7 +67,7 @@ class TestStaffingPlan(unittest.TestCase): if frappe.db.exists("Staffing Plan", "Test 1"): return staffing_plan = frappe.new_doc("Staffing Plan") - staffing_plan.company = "_Test Company 3" + staffing_plan.company = "_Test Company 10" staffing_plan.name = "Test 1" staffing_plan.from_date = nowdate() staffing_plan.to_date = add_days(nowdate(), 10) @@ -85,11 +85,11 @@ def _set_up(): make_company() def make_company(): - if frappe.db.exists("Company", "_Test Company 3"): + if frappe.db.exists("Company", "_Test Company 10"): return company = frappe.new_doc("Company") - company.company_name = "_Test Company 3" - company.abbr = "_TC3" + company.company_name = "_Test Company 10" + company.abbr = "_TC10" company.parent_company = "_Test Company" company.default_currency = "INR" company.country = "India" diff --git a/erpnext/setup/doctype/company/test_records.json b/erpnext/setup/doctype/company/test_records.json index bf9d4bdd19..58d8b5c334 100644 --- a/erpnext/setup/doctype/company/test_records.json +++ b/erpnext/setup/doctype/company/test_records.json @@ -1,66 +1,66 @@ [ - { - "abbr": "_TC", - "company_name": "_Test Company", - "country": "India", - "default_currency": "INR", - "doctype": "Company", - "domain": "Manufacturing", - "chart_of_accounts": "Standard", - "default_holiday_list": "_Test Holiday List" - }, - { - "abbr": "_TC1", - "company_name": "_Test Company 1", - "country": "United States", - "default_currency": "USD", - "doctype": "Company", - "domain": "Retail", - "chart_of_accounts": "Standard", - "default_holiday_list": "_Test Holiday List" - }, - { - "abbr": "_TC2", - "company_name": "_Test Company 2", - "default_currency": "EUR", - "country": "Germany", - "doctype": "Company", - "domain": "Retail", - "chart_of_accounts": "Standard", - "default_holiday_list": "_Test Holiday List" - }, - { - "abbr": "_TC3", - "company_name": "_Test Company 3", - "is_group": 1, - "country": "India", - "default_currency": "INR", - "doctype": "Company", - "domain": "Manufacturing", - "chart_of_accounts": "Standard", - "default_holiday_list": "_Test Holiday List" - }, - { - "abbr": "_TC4", - "company_name": "_Test Company 4", - "parent_company": "_Test Company 3", - "is_group": 1, - "country": "India", - "default_currency": "INR", - "doctype": "Company", - "domain": "Manufacturing", - "chart_of_accounts": "Standard", - "default_holiday_list": "_Test Holiday List" - }, - { - "abbr": "_TC5", - "company_name": "_Test Company 5", - "parent_company": "_Test Company 4", - "country": "India", - "default_currency": "INR", - "doctype": "Company", - "domain": "Manufacturing", - "chart_of_accounts": "Standard", - "default_holiday_list": "_Test Holiday List" - } + { + "abbr": "_TC", + "company_name": "_Test Company", + "country": "India", + "default_currency": "INR", + "doctype": "Company", + "domain": "Manufacturing", + "chart_of_accounts": "Standard", + "default_holiday_list": "_Test Holiday List" + }, + { + "abbr": "_TC1", + "company_name": "_Test Company 1", + "country": "United States", + "default_currency": "USD", + "doctype": "Company", + "domain": "Retail", + "chart_of_accounts": "Standard", + "default_holiday_list": "_Test Holiday List" + }, + { + "abbr": "_TC2", + "company_name": "_Test Company 2", + "default_currency": "EUR", + "country": "Germany", + "doctype": "Company", + "domain": "Retail", + "chart_of_accounts": "Standard", + "default_holiday_list": "_Test Holiday List" + }, + { + "abbr": "_TC3", + "company_name": "_Test Company 3", + "is_group": 1, + "country": "India", + "default_currency": "INR", + "doctype": "Company", + "domain": "Manufacturing", + "chart_of_accounts": "Standard", + "default_holiday_list": "_Test Holiday List" + }, + { + "abbr": "_TC4", + "company_name": "_Test Company 4", + "parent_company": "_Test Company 3", + "is_group": 1, + "country": "India", + "default_currency": "INR", + "doctype": "Company", + "domain": "Manufacturing", + "chart_of_accounts": "Standard", + "default_holiday_list": "_Test Holiday List" + }, + { + "abbr": "_TC5", + "company_name": "_Test Company 5", + "parent_company": "_Test Company 4", + "country": "India", + "default_currency": "INR", + "doctype": "Company", + "domain": "Manufacturing", + "chart_of_accounts": "Standard", + "default_holiday_list": "_Test Holiday List" + } ] From ac199580af30b82bbb5dd77a5c25b4483b4536bb Mon Sep 17 00:00:00 2001 From: Himanshu Date: Mon, 18 Mar 2019 15:44:54 +0530 Subject: [PATCH 101/129] fix(Customer Ledger): ambiguous error in where clause (#16914) fix for error "InternalError: (1052, u"Column 'company' in where clause is ambiguous")" in Customer Ledger Summary --- .../report/customer_ledger_summary/customer_ledger_summary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py index e33bd61411..eceabf56af 100644 --- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py +++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py @@ -195,7 +195,7 @@ class PartyLedgerSummaryReport(object): conditions = [""] if self.filters.company: - conditions.append("company=%(company)s") + conditions.append("gle.company=%(company)s") self.filters.company_finance_book = erpnext.get_default_finance_book(self.filters.company) From 5b43e2f31a187594a16cd52083fe2c8438856285 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 18 Mar 2019 17:00:44 +0530 Subject: [PATCH 102/129] code cleanup --- erpnext/selling/doctype/customer/customer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index ec27498cc7..ba20cbc73e 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -274,7 +274,7 @@ def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=F outstanding_based_on_gle = frappe.db.sql(""" select sum(debit) - sum(credit) from `tabGL Entry` where party_type = 'Customer' - and party = %s and company=%s {0}""".format(cond), (customer, company), debug=1) + and party = %s and company=%s {0}""".format(cond), (customer, company)) outstanding_based_on_gle = flt(outstanding_based_on_gle[0][0]) if outstanding_based_on_gle else 0 From f060831cced725e26fe18a85dd7c24c6fa51b058 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 18 Mar 2019 18:04:34 +0530 Subject: [PATCH 103/129] fix(escaping): make_italian_localization_fields.py --- erpnext/patches/v11_0/make_italian_localization_fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v11_0/make_italian_localization_fields.py b/erpnext/patches/v11_0/make_italian_localization_fields.py index 44a281f86f..d9a7b35e77 100644 --- a/erpnext/patches/v11_0/make_italian_localization_fields.py +++ b/erpnext/patches/v11_0/make_italian_localization_fields.py @@ -19,7 +19,7 @@ def execute(): # Set state codes condition = "" for state, code in state_codes.items(): - condition += " when '{0}' then '{1}'".format(frappe.db.escape(state), frappe.db.escape(code)) + condition += " when {0} then {1}".format(frappe.db.escape(state), frappe.db.escape(code)) if condition: condition = "state_code = (case state {0} end),".format(condition) From 642a5b69f58337db0911704668e532e8f0457ac0 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Mon, 18 Mar 2019 21:53:33 +0530 Subject: [PATCH 104/129] fix: change customer to supplier --- .../report/supplier_ledger_summary/supplier_ledger_summary.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js index 6fd16f2090..f81297760e 100644 --- a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js +++ b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js @@ -35,9 +35,9 @@ frappe.query_reports["Supplier Ledger Summary"] = { }, { "fieldname":"party", - "label": __("Customer"), + "label": __("Supplier"), "fieldtype": "Link", - "options": "Customer", + "options": "Supplier", on_change: () => { var party = frappe.query_report.get_filter_value('party'); if (party) { From 5f8b358fd4746394cbe5c349b0e5bd1280c5af3a Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 19 Mar 2019 11:48:32 +0530 Subject: [PATCH 105/129] Website: Product Configurator and Bootstrap 4 (#15965) - Refactored Homepage with customisable Hero Section - New Homepage Section to add content on Homepage as cards or using Custom HTML - Products page at "/all-products" with customisable filters - Item Configure dialog to find an Item Variant filtered by attribute values - Contact Us dialog on Item page - Customisable Item page content using the Website Content field --- erpnext/config/website.py | 10 + erpnext/hooks.py | 3 +- erpnext/hr/doctype/job_opening/job_opening.py | 23 + .../templates/job_opening_row.html | 9 + erpnext/patches.txt | 2 +- .../add_variant_of_in_item_attribute_table.py | 8 + .../v12_0/set_default_homepage_type.py | 4 + erpnext/portal/doctype/homepage/homepage.js | 7 +- erpnext/portal/doctype/homepage/homepage.json | 277 +++- erpnext/portal/doctype/homepage/homepage.py | 2 - .../portal/doctype/homepage/test_homepage.py | 19 + .../doctype/homepage_section/__init__.py | 0 .../homepage_section/homepage_section.js | 6 + .../homepage_section/homepage_section.json | 336 +++++ .../homepage_section/homepage_section.py | 12 + .../homepage_section/test_homepage_section.py | 76 + .../doctype/homepage_section_card/__init__.py | 0 .../homepage_section_card.json | 203 +++ .../homepage_section_card.py | 9 + .../products_settings/products_settings.js | 11 + .../products_settings/products_settings.json | 604 ++++---- .../products_settings/products_settings.py | 21 + .../doctype/website_attribute/__init__.py | 0 .../website_attribute/website_attribute.json | 76 + .../website_attribute/website_attribute.py | 9 + .../doctype/website_filter_field/__init__.py | 0 .../website_filter_field.json | 76 + .../website_filter_field.py | 9 + .../portal/product_configurator/__init__.py | 0 .../item_variants_cache.py | 94 ++ .../test_product_configurator.py | 84 ++ erpnext/portal/product_configurator/utils.py | 402 ++++++ erpnext/public/build.json | 2 +- erpnext/public/js/shopping_cart.js | 10 +- erpnext/public/js/templates/address_list.html | 4 +- erpnext/public/js/templates/contact_list.html | 4 +- erpnext/public/js/website_theme.js | 17 + erpnext/public/less/products.less | 69 + erpnext/public/less/website.less | 29 +- erpnext/public/node_modules | 1 + erpnext/public/scss/website.scss | 53 + .../quotation_item/quotation_item.json | 146 +- .../setup/doctype/item_group/item_group.py | 20 +- .../operations/default_website.py | 2 +- erpnext/shopping_cart/cart.py | 55 +- .../shopping_cart_settings.json | 1238 +++++++++-------- erpnext/shopping_cart/product_info.py | 4 +- erpnext/stock/doctype/item/item.js | 4 + erpnext/stock/doctype/item/item.json | 74 +- erpnext/stock/doctype/item/item.py | 101 +- erpnext/stock/doctype/item/test_records.json | 3 +- .../doctype/item_attribute/item_attribute.js | 6 + .../item_attribute/item_attribute.json | 101 +- .../item_variant_attribute.json | 57 +- erpnext/templates/generators/item.html | 143 -- erpnext/templates/generators/item/item.html | 32 + .../generators/item/item_add_to_cart.html | 67 + .../generators/item/item_configure.html | 23 + .../generators/item/item_configure.js | 318 +++++ .../generators/item/item_details.html | 22 + .../templates/generators/item/item_image.html | 107 ++ .../templates/generators/item/item_inquiry.js | 70 + .../generators/item/item_specifications.html | 16 + erpnext/templates/generators/item_group.html | 41 +- erpnext/templates/includes/address_row.html | 12 +- erpnext/templates/includes/cart.js | 97 +- .../templates/includes/cart/address_card.html | 12 + .../templates/includes/cart/cart_address.html | 161 ++- .../templates/includes/cart/cart_items.html | 69 +- .../includes/footer/footer_extension.html | 17 +- .../includes/footer/footer_powered.html | 3 +- erpnext/templates/includes/macros.html | 43 +- .../includes/navbar/navbar_items.html | 12 +- .../includes/order/order_macros.html | 8 +- .../templates/includes/order/order_taxes.html | 44 +- erpnext/templates/includes/product_page.js | 215 --- erpnext/templates/pages/cart.html | 138 +- erpnext/templates/pages/help.html | 2 +- erpnext/templates/pages/home.css | 9 + erpnext/templates/pages/home.html | 130 +- erpnext/templates/pages/home.py | 45 +- .../pages/material_request_info.html | 4 +- .../pages/non_profit/leave-chapter.html | 2 +- erpnext/templates/pages/order.html | 40 +- erpnext/templates/pages/product_search.html | 2 +- erpnext/templates/pages/projects.html | 4 +- erpnext/templates/pages/task_info.html | 4 +- erpnext/www/all-products/__init__.py | 0 erpnext/www/all-products/index.html | 163 +++ erpnext/www/all-products/index.js | 161 +++ erpnext/www/all-products/index.py | 26 + erpnext/www/all-products/item_row.html | 24 + erpnext/www/all-products/not_found.html | 1 + 93 files changed, 5057 insertions(+), 1622 deletions(-) create mode 100644 erpnext/hr/doctype/job_opening/templates/job_opening_row.html create mode 100644 erpnext/patches/v12_0/add_variant_of_in_item_attribute_table.py create mode 100644 erpnext/patches/v12_0/set_default_homepage_type.py create mode 100644 erpnext/portal/doctype/homepage/test_homepage.py create mode 100644 erpnext/portal/doctype/homepage_section/__init__.py create mode 100644 erpnext/portal/doctype/homepage_section/homepage_section.js create mode 100644 erpnext/portal/doctype/homepage_section/homepage_section.json create mode 100644 erpnext/portal/doctype/homepage_section/homepage_section.py create mode 100644 erpnext/portal/doctype/homepage_section/test_homepage_section.py create mode 100644 erpnext/portal/doctype/homepage_section_card/__init__.py create mode 100644 erpnext/portal/doctype/homepage_section_card/homepage_section_card.json create mode 100644 erpnext/portal/doctype/homepage_section_card/homepage_section_card.py create mode 100644 erpnext/portal/doctype/website_attribute/__init__.py create mode 100644 erpnext/portal/doctype/website_attribute/website_attribute.json create mode 100644 erpnext/portal/doctype/website_attribute/website_attribute.py create mode 100644 erpnext/portal/doctype/website_filter_field/__init__.py create mode 100644 erpnext/portal/doctype/website_filter_field/website_filter_field.json create mode 100644 erpnext/portal/doctype/website_filter_field/website_filter_field.py create mode 100644 erpnext/portal/product_configurator/__init__.py create mode 100644 erpnext/portal/product_configurator/item_variants_cache.py create mode 100644 erpnext/portal/product_configurator/test_product_configurator.py create mode 100644 erpnext/portal/product_configurator/utils.py create mode 100644 erpnext/public/js/website_theme.js create mode 100644 erpnext/public/less/products.less create mode 120000 erpnext/public/node_modules create mode 100644 erpnext/public/scss/website.scss create mode 100644 erpnext/stock/doctype/item_attribute/item_attribute.js delete mode 100644 erpnext/templates/generators/item.html create mode 100644 erpnext/templates/generators/item/item.html create mode 100644 erpnext/templates/generators/item/item_add_to_cart.html create mode 100644 erpnext/templates/generators/item/item_configure.html create mode 100644 erpnext/templates/generators/item/item_configure.js create mode 100644 erpnext/templates/generators/item/item_details.html create mode 100644 erpnext/templates/generators/item/item_image.html create mode 100644 erpnext/templates/generators/item/item_inquiry.js create mode 100644 erpnext/templates/generators/item/item_specifications.html create mode 100644 erpnext/templates/includes/cart/address_card.html delete mode 100644 erpnext/templates/includes/product_page.js create mode 100644 erpnext/templates/pages/home.css create mode 100644 erpnext/www/all-products/__init__.py create mode 100644 erpnext/www/all-products/index.html create mode 100644 erpnext/www/all-products/index.js create mode 100644 erpnext/www/all-products/index.py create mode 100644 erpnext/www/all-products/item_row.html create mode 100644 erpnext/www/all-products/not_found.html diff --git a/erpnext/config/website.py b/erpnext/config/website.py index 59e7d404d4..d31b057881 100644 --- a/erpnext/config/website.py +++ b/erpnext/config/website.py @@ -11,6 +11,16 @@ def get_data(): "name": "Homepage", "description": _("Settings for website homepage"), }, + { + "type": "doctype", + "name": "Homepage Section", + "description": _("Add cards or custom sections on homepage"), + }, + { + "type": "doctype", + "name": "Products Settings", + "description": _("Settings for website product listing"), + }, { "type": "doctype", "name": "Shopping Cart Settings", diff --git a/erpnext/hooks.py b/erpnext/hooks.py index a6876ac1f3..d28666c48b 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -22,7 +22,8 @@ web_include_css = "assets/css/erpnext-web.css" doctype_js = { "Communication": "public/js/communication.js", - "Event": "public/js/event.js" + "Event": "public/js/event.js", + "Website Theme": "public/js/website_theme.js" } welcome_email = "erpnext.setup.utils.welcome_email" diff --git a/erpnext/hr/doctype/job_opening/job_opening.py b/erpnext/hr/doctype/job_opening/job_opening.py index 4fc2ac1ded..00883d75f1 100644 --- a/erpnext/hr/doctype/job_opening/job_opening.py +++ b/erpnext/hr/doctype/job_opening/job_opening.py @@ -53,3 +53,26 @@ class JobOpening(WebsiteGenerator): def get_list_context(context): context.title = _("Jobs") context.introduction = _('Current Job Openings') + context.get_list = get_job_openings + +def get_job_openings(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by=None): + fields = ['name', 'status', 'job_title', 'description'] + + filters = filters or {} + filters.update({ + 'status': 'Open' + }) + + if txt: + filters.update({ + 'job_title': ['like', '%{0}%'.format(txt)], + 'description': ['like', '%{0}%'.format(txt)] + }) + + return frappe.get_all(doctype, + filters, + fields, + start=limit_start, + page_length=limit_page_length, + order_by=order_by + ) diff --git a/erpnext/hr/doctype/job_opening/templates/job_opening_row.html b/erpnext/hr/doctype/job_opening/templates/job_opening_row.html new file mode 100644 index 0000000000..5da8cc82a2 --- /dev/null +++ b/erpnext/hr/doctype/job_opening/templates/job_opening_row.html @@ -0,0 +1,9 @@ +
+

{{ doc.job_title }}

+

{{ doc.description }}

+ +
diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b7e673da34..cb1e77c73f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -577,6 +577,7 @@ erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019 erpnext.patches.v11_0.update_delivery_trip_status erpnext.patches.v11_0.set_missing_gst_hsn_code erpnext.patches.v11_0.rename_bom_wo_fields +erpnext.patches.v12_0.set_default_homepage_type erpnext.patches.v11_0.rename_additional_salary_component_additional_salary erpnext.patches.v11_0.renamed_from_to_fields_in_project erpnext.patches.v11_0.add_permissions_in_gst_settings @@ -584,5 +585,4 @@ erpnext.patches.v11_1.setup_guardian_role execute:frappe.delete_doc('DocType', 'Notification Control') erpnext.patches.v11_0.remove_barcodes_field_from_copy_fields_to_variants erpnext.patches.v12_0.set_task_status -erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019 erpnext.patches.v11_0.make_italian_localization_fields # 01-03-2019 diff --git a/erpnext/patches/v12_0/add_variant_of_in_item_attribute_table.py b/erpnext/patches/v12_0/add_variant_of_in_item_attribute_table.py new file mode 100644 index 0000000000..bc6119067c --- /dev/null +++ b/erpnext/patches/v12_0/add_variant_of_in_item_attribute_table.py @@ -0,0 +1,8 @@ +import frappe + +def execute(): + frappe.db.sql(''' + UPDATE `tabItem Variant Attribute` t1 + INNER JOIN `tabItem` t2 ON t2.name = t1.parent + SET t1.variant_of = t2.variant_of + ''') diff --git a/erpnext/patches/v12_0/set_default_homepage_type.py b/erpnext/patches/v12_0/set_default_homepage_type.py new file mode 100644 index 0000000000..241e4b9b5e --- /dev/null +++ b/erpnext/patches/v12_0/set_default_homepage_type.py @@ -0,0 +1,4 @@ +import frappe + +def execute(): + frappe.db.set_value('Homepage', 'Homepage', 'hero_section_based_on', 'Default') \ No newline at end of file diff --git a/erpnext/portal/doctype/homepage/homepage.js b/erpnext/portal/doctype/homepage/homepage.js index 0b07814f75..ca34d69576 100644 --- a/erpnext/portal/doctype/homepage/homepage.js +++ b/erpnext/portal/doctype/homepage/homepage.js @@ -11,7 +11,12 @@ frappe.ui.form.on('Homepage', { }, refresh: function(frm) { - + frm.add_custom_button(__('Set Meta Tags'), () => { + frappe.utils.set_meta_tag('home'); + }); + frm.add_custom_button(__('Customize Homepage Sections'), () => { + frappe.set_route('List', 'Homepage Section', 'List'); + }); }, }); diff --git a/erpnext/portal/doctype/homepage/homepage.json b/erpnext/portal/doctype/homepage/homepage.json index 81433b1c5d..ad27278dc6 100644 --- a/erpnext/portal/doctype/homepage/homepage.json +++ b/erpnext/portal/doctype/homepage/homepage.json @@ -1,5 +1,7 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "autoname": "", @@ -10,18 +12,24 @@ "doctype": "DocType", "document_type": "Setup", "editable_grid": 0, + "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "company", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_list_view": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, "label": "Company", "length": 0, "no_copy": 0, @@ -31,24 +39,63 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "title", - "fieldtype": "Data", + "columns": 0, + "fieldname": "hero_section_based_on", + "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, - "label": "TItle", + "in_standard_filter": 0, + "label": "Hero Section Based On", + "length": 0, + "no_copy": 0, + "options": "Default\nSlideshow\nHomepage Section", + "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": "column_break_2", + "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, @@ -56,16 +103,88 @@ "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": "title", + "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": "Title", + "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": "section_break_4", + "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": "Hero Section", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.hero_section_based_on === 'Default'", "description": "Company Tagline for website homepage", "fieldname": "tag_line", "fieldtype": "Data", @@ -73,7 +192,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_list_view": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, "label": "Tag Line", "length": 0, "no_copy": 0, @@ -82,16 +203,22 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.hero_section_based_on === 'Default'", "description": "Company Description for website homepage", "fieldname": "description", "fieldtype": "Text", @@ -99,7 +226,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_list_view": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, "label": "Description", "length": 0, "no_copy": 0, @@ -108,23 +237,133 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.hero_section_based_on === 'Default'", + "fieldname": "hero_image", + "fieldtype": "Attach Image", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Hero Image", + "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.hero_section_based_on === 'Slideshow'", + "description": "", + "fieldname": "slideshow", + "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": "Homepage Slideshow", + "length": 0, + "no_copy": 0, + "options": "Website Slideshow", + "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.hero_section_based_on === 'Homepage Section'", + "fieldname": "hero_section", + "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": "Homepage Section", + "length": 0, + "no_copy": 0, + "options": "Homepage Section", + "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": "products_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": "Products", "length": 0, "no_copy": 0, @@ -133,16 +372,21 @@ "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": "/products", "fieldname": "products_url", "fieldtype": "Data", @@ -150,7 +394,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "URL for \"All Products\"", "length": 0, "no_copy": 0, @@ -159,16 +405,21 @@ "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": "Products to be shown on website homepage", "fieldname": "products", "fieldtype": "Table", @@ -176,7 +427,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Products", "length": 0, "no_copy": 0, @@ -186,14 +439,17 @@ "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, "width": "40px" } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 0, @@ -203,7 +459,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2016-08-29 01:28:00.961623", + "modified": "2019-03-02 23:12:59.676202", "modified_by": "Administrator", "module": "Portal", "name": "Homepage", @@ -212,7 +468,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -232,7 +487,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -254,8 +508,11 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", "title_field": "company", - "track_seen": 0 + "track_changes": 1, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/portal/doctype/homepage/homepage.py b/erpnext/portal/doctype/homepage/homepage.py index f8f73fdcd0..4e4d4774ab 100644 --- a/erpnext/portal/doctype/homepage/homepage.py +++ b/erpnext/portal/doctype/homepage/homepage.py @@ -9,8 +9,6 @@ from frappe.website.utils import delete_page_cache class Homepage(Document): def validate(self): - if not self.products: - self.setup_items() if not self.description: self.description = frappe._("This is an example website auto-generated from ERPNext") delete_page_cache('home') diff --git a/erpnext/portal/doctype/homepage/test_homepage.py b/erpnext/portal/doctype/homepage/test_homepage.py new file mode 100644 index 0000000000..b262c4640c --- /dev/null +++ b/erpnext/portal/doctype/homepage/test_homepage.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +from frappe.tests.test_website import set_request +from frappe.website.render import render + +class TestHomepage(unittest.TestCase): + def test_homepage_load(self): + set_request(method='GET', path='home') + response = render() + + self.assertEquals(response.status_code, 200) + + html = frappe.safe_decode(response.get_data()) + self.assertTrue('
', + }).insert() + + set_request(method='GET', path='home') + response = render() + + self.assertEquals(response.status_code, 200) + + html = frappe.safe_decode(response.get_data()) + + soup = BeautifulSoup(html, 'html.parser') + sections = soup.find('main').find_all(class_='custom-section') + self.assertEqual(len(sections), 1) + + homepage_section = sections[0] + self.assertEqual(homepage_section.text, 'My custom html') + + # cleanup + frappe.db.rollback() diff --git a/erpnext/portal/doctype/homepage_section_card/__init__.py b/erpnext/portal/doctype/homepage_section_card/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/portal/doctype/homepage_section_card/homepage_section_card.json b/erpnext/portal/doctype/homepage_section_card/homepage_section_card.json new file mode 100644 index 0000000000..9092b268c5 --- /dev/null +++ b/erpnext/portal/doctype/homepage_section_card/homepage_section_card.json @@ -0,0 +1,203 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2019-02-10 19:39:02.734686", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "title", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Title", + "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": 1, + "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": "subtitle", + "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": "Subtitle", + "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": "image", + "fieldtype": "Attach Image", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Image", + "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": "content", + "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Content", + "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": "route", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Route", + "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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-02-10 20:11:41.040716", + "modified_by": "Administrator", + "module": "Portal", + "name": "Homepage Section Card", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/portal/doctype/homepage_section_card/homepage_section_card.py b/erpnext/portal/doctype/homepage_section_card/homepage_section_card.py new file mode 100644 index 0000000000..bd17279f99 --- /dev/null +++ b/erpnext/portal/doctype/homepage_section_card/homepage_section_card.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class HomepageSectionCard(Document): + pass diff --git a/erpnext/portal/doctype/products_settings/products_settings.js b/erpnext/portal/doctype/products_settings/products_settings.js index 7a57abaf9b..b68b5d7aa8 100644 --- a/erpnext/portal/doctype/products_settings/products_settings.js +++ b/erpnext/portal/doctype/products_settings/products_settings.js @@ -3,6 +3,17 @@ frappe.ui.form.on('Products Settings', { refresh: function(frm) { + frappe.model.with_doctype('Item', () => { + const item_meta = frappe.get_meta('Item'); + const valid_fields = item_meta.fields.filter( + df => ['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden + ).map(df => ({ label: df.label, value: df.fieldname })); + + const field = frappe.meta.get_docfield("Website Filter Field", "fieldname", frm.docname); + field.fieldtype = 'Select'; + field.options = valid_fields; + frm.fields_dict.filter_fields.grid.refresh(); + }); } }); diff --git a/erpnext/portal/doctype/products_settings/products_settings.json b/erpnext/portal/doctype/products_settings/products_settings.json index 69abae13a4..2cf8431497 100644 --- a/erpnext/portal/doctype/products_settings/products_settings.json +++ b/erpnext/portal/doctype/products_settings/products_settings.json @@ -1,255 +1,389 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-04-22 09:11:55.272398", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 0, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2016-04-22 09:11:55.272398", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 0, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If checked, the Home page will be the default Item Group for the website", - "fieldname": "home_page_is_products", - "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": "Home Page is Products", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "If checked, the Home page will be the default Item Group for the website", + "fieldname": "home_page_is_products", + "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": "Home Page is Products", + "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": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "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": "products_as_list", - "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": "Show Products as a List", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "show_availability_status", + "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": "Show Availability Status", + "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": "show_availability_status", - "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": "Show Availability Status", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_5", + "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": "Product Page", + "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": "section_break_5", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "6", + "fieldname": "products_per_page", + "fieldtype": "Int", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Products per Page", + "length": 0, + "no_copy": 0, + "options": "", + "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": "6", - "fieldname": "products_per_page", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Products per Page", - "length": 0, - "no_copy": 0, - "options": "", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "enable_field_filters", + "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": "Enable Field Filters", + "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": "enable_field_filters", + "fieldname": "filter_fields", + "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": "Item Fields", + "length": 0, + "no_copy": 0, + "options": "Website Filter Field", + "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": "enable_attribute_filters", + "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": "Enable Attribute Filters", + "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": "enable_attribute_filters", + "fieldname": "filter_attributes", + "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": "Attributes", + "length": 0, + "no_copy": 0, + "options": "Website Attribute", + "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": "hide_variants", + "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": "Hide Variants", + "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 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-14 17:59:58.473100", - "modified_by": "Administrator", - "module": "Portal", - "name": "Products Settings", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2019-03-07 19:18:31.822309", + "modified_by": "Administrator", + "module": "Portal", + "name": "Products Settings", + "name_case": "", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Website Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Website Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/portal/doctype/products_settings/products_settings.py b/erpnext/portal/doctype/products_settings/products_settings.py index f17ae9fee9..82afebf2f1 100644 --- a/erpnext/portal/doctype/products_settings/products_settings.py +++ b/erpnext/portal/doctype/products_settings/products_settings.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import cint +from frappe import _ from frappe.model.document import Document class ProductsSettings(Document): @@ -14,6 +15,26 @@ class ProductsSettings(Document): website_settings.home_page = 'products' website_settings.save() + self.validate_field_filters() + self.validate_attribute_filters() + + def validate_field_filters(self): + if not (self.enable_field_filters and self.filter_fields): return + + item_meta = frappe.get_meta('Item') + valid_fields = [df.fieldname for df in item_meta.fields if df.fieldtype in ['Link', 'Table MultiSelect']] + + for f in self.filter_fields: + if f.fieldname not in valid_fields: + frappe.throw(_('Filter Fields Row #{0}: Fieldname {1} must be of type "Link" or "Table MultiSelect"').format(f.idx, f.fieldname)) + + def validate_attribute_filters(self): + if not (self.enable_attribute_filters and self.filter_attributes): return + + # if attribute filters are enabled, hide_variants should be disabled + self.hide_variants = 0 + + def home_page_is_products(doc, method): '''Called on saving Website Settings''' home_page_is_products = cint(frappe.db.get_single_value('Products Settings', 'home_page_is_products')) diff --git a/erpnext/portal/doctype/website_attribute/__init__.py b/erpnext/portal/doctype/website_attribute/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/portal/doctype/website_attribute/website_attribute.json b/erpnext/portal/doctype/website_attribute/website_attribute.json new file mode 100644 index 0000000000..2874dc432c --- /dev/null +++ b/erpnext/portal/doctype/website_attribute/website_attribute.json @@ -0,0 +1,76 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2019-01-01 13:04:54.479079", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "attribute", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Attribute", + "length": 0, + "no_copy": 0, + "options": "Item Attribute", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-01-01 13:04:59.715572", + "modified_by": "Administrator", + "module": "Portal", + "name": "Website Attribute", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/portal/doctype/website_attribute/website_attribute.py b/erpnext/portal/doctype/website_attribute/website_attribute.py new file mode 100644 index 0000000000..b8b667a613 --- /dev/null +++ b/erpnext/portal/doctype/website_attribute/website_attribute.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class WebsiteAttribute(Document): + pass diff --git a/erpnext/portal/doctype/website_filter_field/__init__.py b/erpnext/portal/doctype/website_filter_field/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/portal/doctype/website_filter_field/website_filter_field.json b/erpnext/portal/doctype/website_filter_field/website_filter_field.json new file mode 100644 index 0000000000..67c0d0ae73 --- /dev/null +++ b/erpnext/portal/doctype/website_filter_field/website_filter_field.json @@ -0,0 +1,76 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-12-31 17:06:08.716134", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "fieldname", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Fieldname", + "length": 0, + "no_copy": 0, + "options": "", + "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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-01-01 18:26:11.550380", + "modified_by": "Administrator", + "module": "Portal", + "name": "Website Filter Field", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/portal/doctype/website_filter_field/website_filter_field.py b/erpnext/portal/doctype/website_filter_field/website_filter_field.py new file mode 100644 index 0000000000..2aa8a6f98d --- /dev/null +++ b/erpnext/portal/doctype/website_filter_field/website_filter_field.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class WebsiteFilterField(Document): + pass diff --git a/erpnext/portal/product_configurator/__init__.py b/erpnext/portal/product_configurator/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/portal/product_configurator/item_variants_cache.py b/erpnext/portal/product_configurator/item_variants_cache.py new file mode 100644 index 0000000000..cd557b5075 --- /dev/null +++ b/erpnext/portal/product_configurator/item_variants_cache.py @@ -0,0 +1,94 @@ +import frappe + +class ItemVariantsCacheManager: + def __init__(self, item_code): + self.item_code = item_code + + def get_item_variants_data(self): + val = frappe.cache().hget('item_variants_data', self.item_code) + + if not val: + self.build_cache() + + return frappe.cache().hget('item_variants_data', self.item_code) + + + def get_attribute_value_item_map(self): + val = frappe.cache().hget('attribute_value_item_map', self.item_code) + + if not val: + self.build_cache() + + return frappe.cache().hget('attribute_value_item_map', self.item_code) + + + def get_item_attribute_value_map(self): + val = frappe.cache().hget('item_attribute_value_map', self.item_code) + + if not val: + self.build_cache() + + return frappe.cache().hget('item_attribute_value_map', self.item_code) + + + def get_optional_attributes(self): + val = frappe.cache().hget('optional_attributes', self.item_code) + + if not val: + self.build_cache() + + return frappe.cache().hget('optional_attributes', self.item_code) + + + def build_cache(self): + parent_item_code = self.item_code + + attributes = [a.attribute for a in frappe.db.get_all('Item Variant Attribute', + {'parent': parent_item_code}, ['attribute'], order_by='idx asc') + ] + + item_variants_data = frappe.db.get_all('Item Variant Attribute', + {'variant_of': parent_item_code}, ['parent', 'attribute', 'attribute_value'], + order_by='parent', + as_list=1 + ) + + attribute_value_item_map = frappe._dict({}) + item_attribute_value_map = frappe._dict({}) + + for row in item_variants_data: + item_code, attribute, attribute_value = row + # (attr, value) => [item1, item2] + attribute_value_item_map.setdefault((attribute, attribute_value), []).append(item_code) + # item => {attr1: value1, attr2: value2} + item_attribute_value_map.setdefault(item_code, {})[attribute] = attribute_value + + optional_attributes = set() + for item_code, attr_dict in item_attribute_value_map.items(): + for attribute in attributes: + if attribute not in attr_dict: + optional_attributes.add(attribute) + + frappe.cache().hset('attribute_value_item_map', parent_item_code, attribute_value_item_map) + frappe.cache().hset('item_attribute_value_map', parent_item_code, item_attribute_value_map) + frappe.cache().hset('item_variants_data', parent_item_code, item_variants_data) + frappe.cache().hset('optional_attributes', parent_item_code, optional_attributes) + + def clear_cache(self): + keys = ['attribute_value_item_map', 'item_attribute_value_map', 'item_variants_data', 'optional_attributes'] + + for key in keys: + frappe.cache().hdel(key, self.item_code) + + +def build_cache(item_code): + frappe.cache().hset('item_cache_build_in_progress', item_code, 1) + print('ItemVariantsCacheManager: Building cache for', item_code) + i = ItemVariantsCacheManager(item_code) + i.build_cache() + frappe.cache().hset('item_cache_build_in_progress', item_code, 0) + +def enqueue_build_cache(item_code): + if frappe.cache().hget('item_cache_build_in_progress', item_code): + return + frappe.enqueue(build_cache, item_code=item_code, queue='short') diff --git a/erpnext/portal/product_configurator/test_product_configurator.py b/erpnext/portal/product_configurator/test_product_configurator.py new file mode 100644 index 0000000000..a534e5f838 --- /dev/null +++ b/erpnext/portal/product_configurator/test_product_configurator.py @@ -0,0 +1,84 @@ +from __future__ import unicode_literals + +from bs4 import BeautifulSoup +import frappe, unittest +from frappe.tests.test_website import set_request, get_html_for_route +from frappe.website.render import render +from erpnext.portal.product_configurator.utils import get_products_for_website +from erpnext.stock.doctype.item.test_item import make_item_variant + +test_dependencies = ["Item"] + +class TestProductConfigurator(unittest.TestCase): + def setUp(self): + self.create_variant_item() + + def test_product_list(self): + template_items = frappe.get_all('Item', {'show_in_website': 1}) + variant_items = frappe.get_all('Item', {'show_variant_in_website': 1}) + + products_settings = frappe.get_doc('Products Settings') + products_settings.enable_field_filters = 1 + products_settings.append('filter_fields', {'fieldname': 'item_group'}) + products_settings.append('filter_fields', {'fieldname': 'stock_uom'}) + products_settings.save() + + html = get_html_for_route('all-products') + + soup = BeautifulSoup(html, 'html.parser') + products_list = soup.find(class_='products-list') + items = products_list.find_all(class_='card') + self.assertEqual(len(items), len(template_items + variant_items)) + + items_with_item_group = frappe.get_all('Item', {'item_group': '_Test Item Group Desktops', 'show_in_website': 1}) + variants_with_item_group = frappe.get_all('Item', {'item_group': '_Test Item Group Desktops', 'show_variant_in_website': 1}) + + # mock query params + frappe.form_dict = frappe._dict({ + 'field_filters': '{"item_group":["_Test Item Group Desktops"]}' + }) + html = get_html_for_route('all-products') + soup = BeautifulSoup(html, 'html.parser') + products_list = soup.find(class_='products-list') + items = products_list.find_all(class_='card') + self.assertEqual(len(items), len(items_with_item_group + variants_with_item_group)) + + + def test_get_products_for_website(self): + items = get_products_for_website(attribute_filters={ + 'Test Size': ['Medium'] + }) + self.assertEqual(len(items), 1) + + + def create_variant_item(self): + if not frappe.db.exists('Item', '_Test Variant Item 1'): + frappe.get_doc({ + "description": "_Test Variant Item 12", + "doctype": "Item", + "is_stock_item": 1, + "variant_of": "_Test Variant Item", + "item_code": "_Test Variant Item 1", + "item_group": "_Test Item Group", + "item_name": "_Test Variant Item 1", + "stock_uom": "_Test UOM", + "item_defaults": [{ + "company": "_Test Company", + "default_warehouse": "_Test Warehouse - _TC", + "expense_account": "_Test Account Cost for Goods Sold - _TC", + "buying_cost_center": "_Test Cost Center - _TC", + "selling_cost_center": "_Test Cost Center - _TC", + "income_account": "Sales - _TC" + }], + "attributes": [ + { + "attribute": "Test Size", + "attribute_value": "Medium" + } + ], + "show_variant_in_website": 1 + }).insert() + + + def tearDown(self): + frappe.db.rollback() \ No newline at end of file diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py new file mode 100644 index 0000000000..3594bc4467 --- /dev/null +++ b/erpnext/portal/product_configurator/utils.py @@ -0,0 +1,402 @@ +import frappe +from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager + +def get_field_filter_data(): + product_settings = get_product_settings() + filter_fields = [row.fieldname for row in product_settings.filter_fields] + + meta = frappe.get_meta('Item') + fields = [df for df in meta.fields if df.fieldname in filter_fields] + + filter_data = [] + for f in fields: + doctype = f.get_link_doctype() + + # apply enable/disable filter + meta = frappe.get_meta(doctype) + filters = {} + if meta.has_field('enabled'): + filters['enabled'] = 1 + if meta.has_field('disabled'): + filters['disabled'] = 0 + + values = [d.name for d in frappe.get_all(doctype, filters)] + filter_data.append([f, values]) + + return filter_data + + +def get_attribute_filter_data(): + product_settings = get_product_settings() + attributes = [row.attribute for row in product_settings.filter_attributes] + attribute_docs = [ + frappe.get_doc('Item Attribute', attribute) for attribute in attributes + ] + + # mark attribute values as checked if they are present in the request url + if frappe.form_dict: + for attr in attribute_docs: + if attr.name in frappe.form_dict: + value = frappe.form_dict[attr.name] + if value: + enabled_values = value.split(',') + else: + enabled_values = [] + + for v in enabled_values: + for item_attribute_row in attr.item_attribute_values: + if v == item_attribute_row.attribute_value: + item_attribute_row.checked = True + + return attribute_docs + + +def get_products_for_website(field_filters=None, attribute_filters=None, search=None): + + if attribute_filters: + item_codes = get_item_codes_by_attributes(attribute_filters) + items_by_attributes = get_items([['name', 'in', item_codes]]) + + if field_filters: + items_by_fields = get_items_by_fields(field_filters) + + if attribute_filters and not field_filters: + return items_by_attributes + + if field_filters and not attribute_filters: + return items_by_fields + + if field_filters and attribute_filters: + items_intersection = [] + item_codes_in_attribute = [item.name for item in items_by_attributes] + + for item in items_by_fields: + if item.name in item_codes_in_attribute: + items_intersection.append(item) + + return items_intersection + + if search: + return get_items(search=search) + + return get_items() + + +@frappe.whitelist(allow_guest=True) +def get_products_html_for_website(field_filters=None, attribute_filters=None): + field_filters = frappe.parse_json(field_filters) + attribute_filters = frappe.parse_json(attribute_filters) + + items = get_products_for_website(field_filters, attribute_filters) + html = ''.join(get_html_for_items(items)) + + if not items: + html = frappe.render_template('erpnext/www/all-products/not_found.html', {}) + + return html + + +def get_item_codes_by_attributes(attribute_filters, template_item_code=None): + items = [] + + for attribute, values in attribute_filters.items(): + attribute_values = values + + if not attribute_values: continue + + wheres = [] + query_values = [] + for attribute_value in attribute_values: + wheres.append('( attribute = %s and attribute_value = %s )') + query_values += [attribute, attribute_value] + + attribute_query = ' or '.join(wheres) + + if template_item_code: + variant_of_query = 'AND t2.variant_of = %s' + query_values.append(template_item_code) + else: + variant_of_query = '' + + query = ''' + SELECT + t1.parent + FROM + `tabItem Variant Attribute` t1 + WHERE + 1 = 1 + AND ( + {attribute_query} + ) + AND EXISTS ( + SELECT + 1 + FROM + `tabItem` t2 + WHERE + t2.name = t1.parent + {variant_of_query} + ) + GROUP BY + t1.parent + ORDER BY + NULL + '''.format(attribute_query=attribute_query, variant_of_query=variant_of_query) + + item_codes = set([r[0] for r in frappe.db.sql(query, query_values)]) + items.append(item_codes) + + res = list(set.intersection(*items)) + + return res + + +@frappe.whitelist(allow_guest=True) +def get_attributes_and_values(item_code): + '''Build a list of attributes and their possible values. + This will ignore the values upon selection of which there cannot exist one item. + ''' + item_cache = ItemVariantsCacheManager(item_code) + item_variants_data = item_cache.get_item_variants_data() + + attributes = get_item_attributes(item_code) + attribute_list = [a.attribute for a in attributes] + + valid_options = {} + for item_code, attribute, attribute_value in item_variants_data: + if attribute in attribute_list: + valid_options.setdefault(attribute, set()).add(attribute_value) + + for attr in attributes: + attr['values'] = valid_options.get(attr.attribute, []) + + return attributes + + +@frappe.whitelist(allow_guest=True) +def get_next_attribute_and_values(item_code, selected_attributes): + '''Find the count of Items that match the selected attributes. + Also, find the attribute values that are not applicable for further searching. + If less than equal to 10 items are found, return item_codes of those items. + If one item is matched exactly, return item_code of that item. + ''' + selected_attributes = frappe.parse_json(selected_attributes) + + item_cache = ItemVariantsCacheManager(item_code) + item_variants_data = item_cache.get_item_variants_data() + + attributes = get_item_attributes(item_code) + attribute_list = [a.attribute for a in attributes] + filtered_items = get_items_with_selected_attributes(item_code, selected_attributes) + + next_attribute = None + + for attribute in attribute_list: + if attribute not in selected_attributes: + next_attribute = attribute + break + + valid_options_for_attributes = frappe._dict({}) + + for a in attribute_list: + valid_options_for_attributes[a] = set() + + selected_attribute = selected_attributes.get(a, None) + if selected_attribute: + # already selected attribute values are valid options + valid_options_for_attributes[a].add(selected_attribute) + + for row in item_variants_data: + item_code, attribute, attribute_value = row + if item_code in filtered_items and attribute not in selected_attributes and attribute in attribute_list: + valid_options_for_attributes[attribute].add(attribute_value) + + optional_attributes = item_cache.get_optional_attributes() + exact_match = [] + # search for exact match if all selected attributes are required attributes + if len(selected_attributes.keys()) >= (len(attribute_list) - len(optional_attributes)): + item_attribute_value_map = item_cache.get_item_attribute_value_map() + for item_code, attr_dict in item_attribute_value_map.items(): + if item_code in filtered_items and set(attr_dict.keys()) == set(selected_attributes.keys()): + exact_match.append(item_code) + + filtered_items_count = len(filtered_items) + + # get product info if exact match + from erpnext.shopping_cart.product_info import get_product_info_for_website + if exact_match: + data = get_product_info_for_website(exact_match[0]) + product_info = data.product_info + if not data.cart_settings.show_price: + product_info = None + else: + product_info = None + + return { + 'next_attribute': next_attribute, + 'valid_options_for_attributes': valid_options_for_attributes, + 'filtered_items_count': filtered_items_count, + 'filtered_items': filtered_items if filtered_items_count < 10 else [], + 'exact_match': exact_match, + 'product_info': product_info + } + + +def get_items_with_selected_attributes(item_code, selected_attributes): + item_cache = ItemVariantsCacheManager(item_code) + attribute_value_item_map = item_cache.get_attribute_value_item_map() + + items = [] + for attribute, value in selected_attributes.items(): + items.append(set(attribute_value_item_map[(attribute, value)])) + + return set.intersection(*items) + + +def get_items_by_fields(field_filters): + meta = frappe.get_meta('Item') + filters = [] + for fieldname, values in field_filters.items(): + if not values: continue + + _doctype = 'Item' + _fieldname = fieldname + + df = meta.get_field(fieldname) + if df.fieldtype == 'Table MultiSelect': + child_doctype = df.options + child_meta = frappe.get_meta(child_doctype) + fields = child_meta.get("fields", { "fieldtype": "Link", "in_list_view": 1 }) + if fields: + _doctype = child_doctype + _fieldname = fields[0].fieldname + + if len(values) == 1: + filters.append([_doctype, _fieldname, '=', values[0]]) + else: + filters.append([_doctype, _fieldname, 'in', values]) + + return get_items(filters) + + +def get_items(filters=None, search=None): + start = frappe.form_dict.start or 0 + products_settings = get_product_settings() + page_length = products_settings.products_per_page + + filters = filters or [] + # convert to list of filters + if isinstance(filters, dict): + filters = [['Item', fieldname, '=', value] for fieldname, value in filters.items()] + + show_in_website_condition = '' + if products_settings.hide_variants: + show_in_website_condition = get_conditions({'show_in_website': 1 }, 'and') + else: + show_in_website_condition = get_conditions([ + ['show_in_website', '=', 1], + ['show_variant_in_website', '=', 1] + ], 'or') + + search_condition = '' + if search: + search = '%{}%'.format(search) + or_filters = [ + ['name', 'like', search], + ['item_name', 'like', search], + ['description', 'like', search], + ['item_group', 'like', search] + ] + search_condition = get_conditions(or_filters, 'or') + + filter_condition = get_conditions(filters, 'and') + + where_conditions = ' and '.join( + [condition for condition in [show_in_website_condition, search_condition, filter_condition] if condition] + ) + + left_joins = [] + for f in filters: + if len(f) == 4 and f[0] != 'Item': + left_joins.append(f[0]) + + left_join = ' '.join(['LEFT JOIN `tab{0}` on (`tab{0}`.parent = `tabItem`.name)'.format(l) for l in left_joins]) + + results = frappe.db.sql(''' + SELECT + `tabItem`.`name`, `tabItem`.`item_name`, + `tabItem`.`website_image`, `tabItem`.`image`, + `tabItem`.`web_long_description`, `tabItem`.`description`, + `tabItem`.`route` + FROM + `tabItem` + {left_join} + WHERE + {where_conditions} + GROUP BY + `tabItem`.`name` + ORDER BY + `tabItem`.`weightage` DESC + LIMIT + {page_length} + OFFSET + {start} + '''.format( + where_conditions=where_conditions, + start=start, + page_length=page_length, + left_join=left_join + ) + , as_dict=1) + + for r in results: + r.description = r.web_long_description or r.description + r.image = r.website_image or r.image + + return results + + +def get_conditions(filter_list, and_or='and'): + from frappe.model.db_query import DatabaseQuery + + if not filter_list: + return '' + + conditions = [] + DatabaseQuery('Item').build_filter_conditions(filter_list, conditions, ignore_permissions=True) + join_by = ' {0} '.format(and_or) + + return '(' + join_by.join(conditions) + ')' + +# utilities + +def get_item_attributes(item_code): + attributes = frappe.db.get_all('Item Variant Attribute', + fields=['attribute'], + filters={ + 'parenttype': 'Item', + 'parent': item_code + }, + order_by='idx asc' + ) + + optional_attributes = ItemVariantsCacheManager(item_code).get_optional_attributes() + + for a in attributes: + if a.attribute in optional_attributes: + a.optional = True + + return attributes + +def get_html_for_items(items): + html = [] + for item in items: + html.append(frappe.render_template('erpnext/www/all-products/item_row.html', { + 'item': item + })) + return html + +def get_product_settings(): + doc = frappe.get_cached_doc('Products Settings') + doc.products_per_page = doc.products_per_page or 20 + return doc diff --git a/erpnext/public/build.json b/erpnext/public/build.json index c34eef2508..60e72dad71 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -11,7 +11,7 @@ "public/js/shopping_cart.js" ], "css/erpnext-web.css": [ - "public/less/website.less" + "public/scss/website.scss" ], "js/marketplace.min.js": [ "public/js/hub/marketplace.js" diff --git a/erpnext/public/js/shopping_cart.js b/erpnext/public/js/shopping_cart.js index 7755141da6..5a0526814f 100644 --- a/erpnext/public/js/shopping_cart.js +++ b/erpnext/public/js/shopping_cart.js @@ -48,6 +48,7 @@ $.extend(shopping_cart, { args: { item_code: opts.item_code, qty: opts.qty, + additional_notes: opts.additional_notes !== undefined ? opts.additional_notes : undefined, with_items: opts.with_items || 0 }, btn: opts.btn, @@ -94,11 +95,12 @@ $.extend(shopping_cart, { } }, - shopping_cart_update: function(item_code, newVal, cart_dropdown) { + shopping_cart_update: function({item_code, qty, cart_dropdown, additional_notes}) { frappe.freeze(); shopping_cart.update_cart({ - item_code: item_code, - qty: newVal, + item_code, + qty, + additional_notes, with_items: 1, btn: this, callback: function(r) { @@ -131,7 +133,7 @@ $.extend(shopping_cart, { } input.val(newVal); var item_code = input.attr("data-item-code"); - shopping_cart.shopping_cart_update(item_code, newVal, true); + shopping_cart.shopping_cart_update({item_code, qty: newVal, cart_dropdown: true}); return false; }); diff --git a/erpnext/public/js/templates/address_list.html b/erpnext/public/js/templates/address_list.html index 2379ef6b48..0bc86edb08 100644 --- a/erpnext/public/js/templates/address_list.html +++ b/erpnext/public/js/templates/address_list.html @@ -9,7 +9,7 @@ ({%= __("Shipping") %}){% } %} {%= __("Edit") %}

@@ -19,5 +19,5 @@ {% if(!addr_list.length) { %}

{%= __("No address added yet.") %}

{% } %} -

+

diff --git a/erpnext/public/js/templates/contact_list.html b/erpnext/public/js/templates/contact_list.html index 893b4e0ec2..2144893961 100644 --- a/erpnext/public/js/templates/contact_list.html +++ b/erpnext/public/js/templates/contact_list.html @@ -10,7 +10,7 @@ – {%= contact_list[i].designation %} {% } %} {%= __("Edit") %}

@@ -33,6 +33,6 @@ {% if(!contact_list.length) { %}

{%= __("No contacts added yet.") %}

{% } %} -

\ No newline at end of file diff --git a/erpnext/public/js/website_theme.js b/erpnext/public/js/website_theme.js new file mode 100644 index 0000000000..6c7edfa655 --- /dev/null +++ b/erpnext/public/js/website_theme.js @@ -0,0 +1,17 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +frappe.ui.form.on('Website Theme', { + apply_custom_theme(frm) { + let custom_theme = frm.doc.custom_theme; + custom_theme = custom_theme.split('\n'); + if ( + frm.doc.apply_custom_theme + && custom_theme.length === 2 + && custom_theme[1].includes('frappe/public/scss/website') + ) { + frm.set_value('custom_theme', + `$primary: #7575ff;\n@import "frappe/public/scss/website";\n@import "erpnext/public/scss/website";`); + } + } +}); diff --git a/erpnext/public/less/products.less b/erpnext/public/less/products.less new file mode 100644 index 0000000000..79f57b331a --- /dev/null +++ b/erpnext/public/less/products.less @@ -0,0 +1,69 @@ +@import "variables.less"; + +.products-list .product-image { + display: inline-block; + width: 160px; + height: 160px; + object-fit: contain; + margin-right: 1rem; +} + +.product-image.no-image { + display: flex; + justify-content: center; + align-items: center; + font-size: 3rem; + color: var(--gray); + background: var(--light); +} + +.product-image a { + text-decoration: none; +} + +.filter-options { + max-height: 300px; + overflow: auto; +} + +.item-slideshow-image { + height: 3rem; + width: 3rem; + object-fit: contain; + padding: 0.5rem; + border: 1px solid @border-color; + border-radius: 4px; + cursor: pointer; + + &:hover, &.active { + border-color: var(--primary); + } +} + +.address-card { + cursor: pointer; + position: relative; + + .check { + display: none; + } + + &.active { + border-color: var(--primary); + + .check { + display: inline-flex; + } + } +} + +.check { + display: inline-flex; + padding: 0.25rem; + background: var(--primary); + color: white; + border-radius: 50%; + font-size: 12px; + width: 24px; + height: 24px; +} \ No newline at end of file diff --git a/erpnext/public/less/website.less b/erpnext/public/less/website.less index 0518b26452..57a0a332a9 100644 --- a/erpnext/public/less/website.less +++ b/erpnext/public/less/website.less @@ -245,10 +245,10 @@ } } -.number-spinner { - width:100px; - margin-top:5px; -} +// .number-spinner { +// width:100px; +// margin-top:5px; +// } .cart-btn { border-color: #ccc; @@ -361,3 +361,24 @@ border-color: @brand-primary; } } + +.item-slideshow-image { + height: 3rem; + width: 3rem; + object-fit: contain; + padding: 0.5rem; + border: 1px solid @border-color; + border-radius: 4px; + cursor: pointer; + + &:hover, &.active { + border-color: @brand-primary; + } +} + +.section-products { + .card-img-top { + max-height: 300px; + object-fit: contain; + } +} \ No newline at end of file diff --git a/erpnext/public/node_modules b/erpnext/public/node_modules new file mode 120000 index 0000000000..903b09c1de --- /dev/null +++ b/erpnext/public/node_modules @@ -0,0 +1 @@ +/Users/netchampfaris/frappe-bench/apps/erpnext/node_modules \ No newline at end of file diff --git a/erpnext/public/scss/website.scss b/erpnext/public/scss/website.scss new file mode 100644 index 0000000000..002498f273 --- /dev/null +++ b/erpnext/public/scss/website.scss @@ -0,0 +1,53 @@ +@import "frappe/public/scss/variables"; + +.product-image img { + min-height: 20rem; + max-height: 30rem; +} + +.filter-options { + max-height: 300px; + overflow: auto; +} + +.item-slideshow-image { + height: 3rem; + width: 3rem; + object-fit: contain; + padding: 0.5rem; + border: 1px solid $border-color; + border-radius: 4px; + cursor: pointer; + + &:hover, &.active { + border-color: $primary; + } +} + +.address-card { + cursor: pointer; + position: relative; + + .check { + display: none; + } + + &.active { + border-color: $primary; + + .check { + display: inline-flex; + } + } +} + +.check { + display: inline-flex; + padding: 0.25rem; + background: $primary; + color: white; + border-radius: 50%; + font-size: 12px; + width: 24px; + height: 24px; +} diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index 2eaa7277ca..118c333b16 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -1,18 +1,18 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2013-03-07 11:42:57", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "", + "beta": 0, + "creation": "2013-03-07 11:42:57", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", "fields": [ { "allow_bulk_edit": 0, @@ -1934,32 +1934,96 @@ "translatable": 0, "unique": 0, "width": "150px" + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "shopping_cart_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": "Shopping Cart", + "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": "additional_notes", + "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Additional Notes", + "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 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "menu_index": 0, - "modified": "2019-02-18 18:57:25.277633", - "modified_by": "Administrator", - "module": "Selling", - "name": "Quotation Item", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 1, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "menu_index": 0, + "modified": "2019-01-09 17:49:41.606821", + "modified_by": "Administrator", + "module": "Selling", + "name": "Quotation Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, "track_views": 0 -} +} \ No newline at end of file diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 527bed2392..0395d1de8c 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -71,8 +71,7 @@ class ItemGroup(NestedSet, WebsiteGenerator): "items": get_product_list_for_group(product_group = self.name, start=start, limit=context.page_length + 1, search=frappe.form_dict.get("search")), "parents": get_parent_item_groups(self.parent_item_group), - "title": self.name, - "products_as_list": cint(frappe.db.get_single_value('Website Settings', 'products_as_list')) + "title": self.name }) if self.slideshow: @@ -119,7 +118,7 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non for item in data: set_product_info_for_website(item) - return [get_item_for_list_in_html(r) for r in data] + return data def get_child_groups_for_list_in_html(item_group, start, limit, search): search_filters = None @@ -141,7 +140,7 @@ def get_child_groups_for_list_in_html(item_group, start, limit, search): limit = limit ) - return [get_item_for_list_in_html(r) for r in data] + return data def adjust_qty_for_expired_items(data): adjusted_data = [] @@ -172,9 +171,7 @@ def get_item_for_list_in_html(context): context["show_availability_status"] = cint(frappe.db.get_single_value('Products Settings', 'show_availability_status')) - products_template = 'templates/includes/products_as_grid.html' - if cint(frappe.db.get_single_value('Products Settings', 'products_as_list')): - products_template = 'templates/includes/products_as_list.html' + products_template = 'templates/includes/products_as_list.html' return frappe.get_template(products_template).render(context) @@ -188,15 +185,20 @@ def get_group_item_count(item_group): def get_parent_item_groups(item_group_name): + base_parents = [ + {"name": frappe._("Home"), "route":"/"}, + {"name": frappe._("All Products"), "route":"/all-products"}, + ] if not item_group_name: - return [{"name": frappe._("Home"), "route":"/"}] + return base_parents + item_group = frappe.get_doc("Item Group", item_group_name) parent_groups = frappe.db.sql("""select name, route from `tabItem Group` where lft <= %s and rgt >= %s and show_in_website=1 order by lft asc""", (item_group.lft, item_group.rgt), as_dict=True) - return [{"name": frappe._("Home"), "route":"/"}] + parent_groups + return base_parents + parent_groups def invalidate_cache_for(doc, item_group=None): if not item_group: diff --git a/erpnext/setup/setup_wizard/operations/default_website.py b/erpnext/setup/setup_wizard/operations/default_website.py index 8ca213b1a0..38b5c1470e 100644 --- a/erpnext/setup/setup_wizard/operations/default_website.py +++ b/erpnext/setup/setup_wizard/operations/default_website.py @@ -45,7 +45,7 @@ class website_maker(object): website_settings.append("top_bar_items", { "doctype": "Top Bar Item", "label": _("Products"), - "url": "/products" + "url": "/all-products" }) website_settings.save() diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 7eb1614350..8e8c79c27a 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -45,7 +45,8 @@ def get_cart_quotation(doc=None): for address in addresses], "billing_addresses": [{"name": address.name, "display": address.display} for address in addresses], - "shipping_rules": get_applicable_shipping_rules(party) + "shipping_rules": get_applicable_shipping_rules(party), + "cart_settings": frappe.get_cached_doc("Shopping Cart Settings") } @frappe.whitelist() @@ -83,7 +84,14 @@ def place_order(): return sales_order.name @frappe.whitelist() -def update_cart(item_code, qty, with_items=False): +def request_for_quotation(): + quotation = _get_cart_quotation() + quotation.flags.ignore_permissions = True + quotation.submit() + return quotation.name + +@frappe.whitelist() +def update_cart(item_code, qty, additional_notes=None, with_items=False): quotation = _get_cart_quotation() empty_card = False @@ -101,10 +109,12 @@ def update_cart(item_code, qty, with_items=False): quotation.append("items", { "doctype": "Quotation Item", "item_code": item_code, - "qty": qty + "qty": qty, + "additional_notes": additional_notes }) else: quotation_items[0].qty = qty + quotation_items[0].additional_notes = additional_notes apply_cart_settings(quotation=quotation) @@ -140,6 +150,45 @@ def get_shopping_cart_menu(context=None): return frappe.render_template('templates/includes/cart/cart_dropdown.html', context) + +@frappe.whitelist() +def add_new_address(doc): + doc = frappe.parse_json(doc) + doc.update({ + 'doctype': 'Address' + }) + address = frappe.get_doc(doc) + address.save(ignore_permissions=True) + + return address + +@frappe.whitelist(allow_guest=True) +def create_lead_for_item_inquiry(lead, subject, message): + lead = frappe.parse_json(lead) + lead_doc = frappe.new_doc('Lead') + lead_doc.update(lead) + lead_doc.set('lead_owner', '') + + try: + lead_doc.save(ignore_permissions=True) + except frappe.exceptions.DuplicateEntryError: + frappe.clear_messages() + lead_doc = frappe.get_doc('Lead', {'email_id': lead['email_id']}) + + lead_doc.add_comment('Comment', text=''' +
+
{subject}
+

{message}

+
+ '''.format(subject=subject, message=message)) + + return lead_doc + + +@frappe.whitelist() +def get_terms_and_conditions(terms_name): + return frappe.db.get_value('Terms and Conditions', terms_name, 'terms') + @frappe.whitelist() def update_cart_address(address_fieldname, address_name): quotation = _get_cart_quotation() diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json index 724c1e9ba2..e6b47a6e73 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json @@ -1,641 +1,683 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-06-19 15:57:32", - "custom": 0, - "description": "Default settings for Shopping Cart", - "docstatus": 0, - "doctype": "DocType", - "document_type": "System", - "editable_grid": 0, + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2013-06-19 15:57:32", + "custom": 0, + "description": "Default settings for Shopping Cart", + "docstatus": 0, + "doctype": "DocType", + "document_type": "System", + "editable_grid": 0, "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "enabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Enable purchase of items via the website", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "enabled", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Enable Shopping Cart", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "display_settings", - "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": "Display Settings", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "description": "", + "fieldname": "display_settings", + "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": "Display Settings", + "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": "", - "fieldname": "show_attachments", - "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": "Show Public Attachments", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "description": "", + "fieldname": "show_attachments", + "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": "Show Public Attachments", + "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.enabled==0", - "description": "", - "fieldname": "show_price", - "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": "Show Price", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "description": "", + "fieldname": "show_price", + "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": "Show Price", + "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": "column_break_5", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "show_stock_availability", + "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": "Show Stock Availability", + "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": "show_stock_availability", - "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": "Show Stock Availability", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "show_configure_button", + "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": "Show Configure Button", + "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": "show_stock_availability", - "fieldname": "show_quantity_in_website", - "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": "Show Stock Quantity", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "show_contact_us_button", + "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": "Show Contact Us Button", + "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": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "show_stock_availability", + "fieldname": "show_quantity_in_website", + "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": "Show Stock Quantity", + "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": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "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, + "depends_on": "enabled", + "fieldname": "section_break_2", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Prices will not be shown if Price List is not set", - "fieldname": "price_list", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 1, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "Prices will not be shown if Price List is not set", + "fieldname": "price_list", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Price List", + "length": 0, + "no_copy": 0, + "options": "Price List", + "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": "default_customer_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Customer Group", - "length": 0, - "no_copy": 0, - "options": "Customer Group", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_4", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "quotation_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Quotation Series", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "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, + "description": "", + "fieldname": "default_customer_group", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 1, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Default Customer Group", + "length": 0, + "no_copy": 0, + "options": "Customer Group", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval:doc.enable_checkout", - "columns": 0, - "fieldname": "section_break_8", - "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": "Checkout Settings", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "quotation_series", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Quotation Series", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "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": "enable_checkout", - "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": "Enable Checkout", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "collapsible_depends_on": "eval:doc.enable_checkout", + "columns": 0, + "depends_on": "enabled", + "fieldname": "section_break_8", + "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": "Checkout Settings", + "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": "Orders", - "description": "After payment completion redirect user to selected page.", - "fieldname": "payment_success_url", - "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": "Payment Success Url", - "length": 0, - "no_copy": 0, - "options": "\nOrders\nInvoices\nMy Account", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": "", + "columns": 0, + "depends_on": "", + "fieldname": "enable_checkout", + "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": "Enable Checkout", + "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": "column_break_11", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Orders", + "description": "After payment completion redirect user to selected page.", + "fieldname": "payment_success_url", + "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": "Payment Success Url", + "length": 0, + "no_copy": 0, + "options": "\nOrders\nInvoices\nMy Account", + "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": "payment_gateway_account", - "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": "Payment Gateway Account", - "length": 0, - "no_copy": 0, - "options": "Payment Gateway Account", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_11", + "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": "payment_gateway_account", + "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": "Payment Gateway Account", + "length": 0, + "no_copy": 0, + "options": "Payment Gateway Account", + "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 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-shopping-cart", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2018-05-31 03:11:58.911732", - "modified_by": "sushant@digithinkit.com", - "module": "Shopping Cart", - "name": "Shopping Cart Settings", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "fa fa-shopping-cart", + "idx": 1, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2019-01-26 13:54:24.575322", + "modified_by": "Administrator", + "module": "Shopping Cart", + "name": "Shopping Cart Settings", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Website Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Website Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0 -} + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_order": "ASC", + "track_changes": 0, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index 3af5afa044..f9a45ce578 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -41,10 +41,10 @@ def get_product_info_for_website(item_code): if item: product_info["qty"] = item[0].qty - return { + return frappe._dict({ "product_info": product_info, "cart_settings": cart_settings - } + }) def set_product_info_for_website(item): """set product price uom for website""" diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 3749601c15..6c30d00d23 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -180,6 +180,10 @@ frappe.ui.form.on("Item", { if (frm.doc.default_warehouse && !frm.doc.website_warehouse){ frm.set_value("website_warehouse", frm.doc.default_warehouse); } + }, + + set_meta_tags(frm) { + frappe.utils.set_meta_tag(frm.doc.route); } }); diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index b637573882..25c7f445da 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -1,7 +1,7 @@ { "allow_copy": 0, "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, + "allow_guest_to_view": 1, "allow_import": 1, "allow_rename": 1, "autoname": "field:item_code", @@ -3860,6 +3860,39 @@ "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.show_in_website || doc.show_variant_in_website", + "fieldname": "set_meta_tags", + "fieldtype": "Button", + "hidden": 0, + "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 Meta Tags", + "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, @@ -3994,6 +4027,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "You can use any valid Bootstrap 4 markup in this field. It will be shown on your Item Page.", + "fieldname": "website_content", + "fieldtype": "HTML Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Website Content", + "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, @@ -4194,7 +4260,7 @@ "unique": 0 } ], - "has_web_view": 0, + "has_web_view": 1, "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-tag", @@ -4206,7 +4272,7 @@ "issingle": 0, "istable": 0, "max_attachments": 1, - "modified": "2019-02-16 17:43:56.039611", + "modified": "2019-03-08 11:47:59.269724", "modified_by": "Administrator", "module": "Stock", "name": "Item", @@ -4377,4 +4443,4 @@ "track_changes": 1, "track_seen": 0, "track_views": 0 -} +} \ No newline at end of file diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 5669552989..2dcecb93b2 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -9,11 +9,11 @@ import erpnext import frappe import copy from erpnext.controllers.item_variant import (ItemVariantExistsError, - copy_attributes_to_variant, get_variant, make_variant_item_code, validate_item_variant_attributes) + copy_attributes_to_variant, get_variant, make_variant_item_code, validate_item_variant_attributes) from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups, invalidate_cache_for) from frappe import _, msgprint from frappe.utils import (cint, cstr, flt, formatdate, get_timestamp, getdate, - now_datetime, random_string, strip) + now_datetime, random_string, strip) from frappe.utils.html_utils import clean_html from frappe.website.doctype.website_slideshow.website_slideshow import \ get_slideshow @@ -40,7 +40,7 @@ class Item(WebsiteGenerator): website = frappe._dict( page_title_field="item_name", condition_field="show_in_website", - template="templates/generators/item.html", + template="templates/generators/item/item.html", no_cache=1 ) @@ -160,7 +160,7 @@ class Item(WebsiteGenerator): '''Add a new price''' if not price_list: price_list = (frappe.db.get_single_value('Selling Settings', 'selling_price_list') - or frappe.db.get_value('Price List', _('Standard Selling'))) + or frappe.db.get_value('Price List', _('Standard Selling'))) if price_list: item_price = frappe.get_doc({ "doctype": "Item Price", @@ -199,7 +199,7 @@ class Item(WebsiteGenerator): def make_route(self): if not self.route: return cstr(frappe.db.get_value('Item Group', self.item_group, - 'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5)) + 'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5)) def validate_website_image(self): """Validate if the website image is a public file""" @@ -222,7 +222,7 @@ class Item(WebsiteGenerator): if not file_doc: if not auto_set_website_image: frappe.msgprint(_("Website Image {0} attached to Item {1} cannot be found") - .format(self.website_image, self.name)) + .format(self.website_image, self.name)) self.website_image = None @@ -313,6 +313,8 @@ class Item(WebsiteGenerator): self.set_variant_context(context) self.set_attribute_context(context) self.set_disabled_attributes(context) + self.set_metatags(context) + self.set_shopping_cart_data(context) return context @@ -323,8 +325,8 @@ class Item(WebsiteGenerator): # load variants # also used in set_attribute_context context.variants = frappe.get_all("Item", - filters={"variant_of": self.name, "show_variant_in_website": 1}, - order_by="name asc") + filters={"variant_of": self.name, "show_variant_in_website": 1}, + order_by="name asc") variant = frappe.form_dict.variant if not variant and context.variants: @@ -335,7 +337,7 @@ class Item(WebsiteGenerator): context.variant = frappe.get_doc("Item", variant) for fieldname in ("website_image", "web_long_description", "description", - "website_specifications"): + "website_specifications"): if context.variant.get(fieldname): value = context.variant.get(fieldname) if isinstance(value, list): @@ -358,8 +360,12 @@ class Item(WebsiteGenerator): # load attributes for v in context.variants: v.attributes = frappe.get_all("Item Variant Attribute", - fields=["attribute", "attribute_value"], + fields=["attribute", "attribute_value"], filters={"parent": v.name}) + # make a map for easier access in templates + v.attribute_map = frappe._dict({}) + for attr in v.attributes: + v.attribute_map[attr.attribute] = attr.attribute_value for attr in v.attributes: values = attribute_values_available.setdefault(attr.attribute, []) @@ -431,6 +437,31 @@ class Item(WebsiteGenerator): if not find_variant(combination): context.disabled_attributes.setdefault(attr.attribute, []).append(combination[-1]) + def set_metatags(self, context): + context.metatags = frappe._dict({}) + + safe_description = frappe.utils.to_markdown(self.description) + + context.metatags.url = frappe.utils.get_url() + '/' + context.route + + if context.website_image: + if context.website_image.startswith('http'): + url = context.website_image + else: + url = frappe.utils.get_url() + context.website_image + context.metatags.image = url + + context.metatags.description = safe_description[:300] + + context.metatags.title = self.item_name or self.item_code + + context.metatags['og:type'] = 'product' + context.metatags['og:site_name'] = 'ERPNext' + + def set_shopping_cart_data(self, context): + from erpnext.shopping_cart.product_info import get_product_info_for_website + context.shopping_cart = get_product_info_for_website(self.name) + def add_default_uom_in_conversion_factor_table(self): uom_conv_list = [d.uom for d in self.get("uoms")] if self.stock_uom not in uom_conv_list: @@ -533,7 +564,7 @@ class Item(WebsiteGenerator): warehouse += [d.get("warehouse")] else: frappe.throw(_("Row {0}: An Reorder entry already exists for this warehouse {1}") - .format(d.idx, d.warehouse), DuplicateReorderRows) + .format(d.idx, d.warehouse), DuplicateReorderRows) if d.warehouse_reorder_level and not d.warehouse_reorder_qty: frappe.throw(_("Row #{0}: Please set reorder quantity").format(d.idx)) @@ -553,7 +584,7 @@ class Item(WebsiteGenerator): def update_item_price(self): frappe.db.sql("""update `tabItem Price` set item_name=%s, item_description=%s, brand=%s where item_code=%s""", - (self.item_name, self.description, self.brand, self.name)) + (self.item_name, self.description, self.brand, self.name)) def on_trash(self): super(Item, self).on_trash() @@ -575,7 +606,7 @@ class Item(WebsiteGenerator): new_properties = [cstr(d) for d in frappe.db.get_value("Item", new_name, field_list)] if new_properties != [cstr(self.get(fld)) for fld in field_list]: frappe.throw(_("To merge, following properties must be same for both items") - + ": \n" + ", ".join([self.meta.get_label(fld) for fld in field_list])) + + ": \n" + ", ".join([self.meta.get_label(fld) for fld in field_list])) def after_rename(self, old_name, new_name, merge): if self.route: @@ -598,7 +629,7 @@ class Item(WebsiteGenerator): item_wise_tax_detail.pop(old_name) frappe.db.set_value(dt, d.name, "item_wise_tax_detail", - json.dumps(item_wise_tax_detail), update_modified=False) + json.dumps(item_wise_tax_detail), update_modified=False) def set_last_purchase_rate(self, new_name): last_purchase_rate = get_last_purchase_details(new_name).get("base_rate", 0) @@ -626,7 +657,7 @@ class Item(WebsiteGenerator): self.set("website_specifications", []) if self.item_group: for label, desc in frappe.db.get_values("Item Website Specification", - {"parent": self.item_group}, ["label", "description"]): + {"parent": self.item_group}, ["label", "description"]): row = self.append("website_specifications") row.label = label row.description = desc @@ -700,7 +731,7 @@ class Item(WebsiteGenerator): def update_variants(self): if self.flags.dont_update_variants or \ - frappe.db.get_single_value('Item Variant Settings', 'do_not_update_variants'): + frappe.db.get_single_value('Item Variant Settings', 'do_not_update_variants'): return if self.has_variants: variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name}) @@ -751,7 +782,7 @@ class Item(WebsiteGenerator): template_uom = frappe.db.get_value("Item", self.variant_of, "stock_uom") if template_uom != self.stock_uom: frappe.throw(_("Default Unit of Measure for Variant '{0}' must be same as in Template '{1}'") - .format(self.stock_uom, template_uom)) + .format(self.stock_uom, template_uom)) def validate_uom_conversion_factor(self): if self.uoms: @@ -783,10 +814,14 @@ class Item(WebsiteGenerator): variant = get_variant(self.variant_of, args, self.name) if variant: frappe.throw(_("Item variant {0} exists with same attributes") - .format(variant), ItemVariantExistsError) + .format(variant), ItemVariantExistsError) validate_item_variant_attributes(self, args) + # copy variant_of value for each attribute row + for d in self.attributes: + d.variant_of = self.variant_of + def get_timeline_data(doctype, name): '''returns timeline data based on stock ledger entry''' @@ -866,18 +901,18 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): limit 1""", (item_code, cstr(doc_name)), as_dict=1) purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date - or "1900-01-01") + or "1900-01-01") purchase_receipt_date = getdate(last_purchase_receipt and - last_purchase_receipt[0].posting_date or "1900-01-01") + last_purchase_receipt[0].posting_date or "1900-01-01") if (purchase_order_date > purchase_receipt_date) or \ - (last_purchase_order and not last_purchase_receipt): + (last_purchase_order and not last_purchase_receipt): # use purchase order last_purchase = last_purchase_order[0] purchase_date = purchase_order_date elif (purchase_receipt_date > purchase_order_date) or \ - (last_purchase_receipt and not last_purchase_order): + (last_purchase_receipt and not last_purchase_order): # use purchase receipt last_purchase = last_purchase_receipt[0] purchase_date = purchase_receipt_date @@ -907,7 +942,7 @@ def invalidate_cache_for_item(doc): invalidate_cache_for(doc, doc.item_group) website_item_groups = list(set((doc.get("old_website_item_groups") or []) - + [d.item_group for d in doc.get({"doctype": "Website Item Group"}) if d.item_group])) + + [d.item_group for d in doc.get({"doctype": "Website Item Group"}) if d.item_group])) for item_group in website_item_groups: invalidate_cache_for(doc, item_group) @@ -915,6 +950,22 @@ def invalidate_cache_for_item(doc): if doc.get("old_item_group") and doc.get("old_item_group") != doc.item_group: invalidate_cache_for(doc, doc.old_item_group) + invalidate_item_variants_cache_for_website(doc) + + +def invalidate_item_variants_cache_for_website(doc): + from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager + + item_code = None + if doc.has_variants and doc.show_in_website: + item_code = doc.name + elif doc.variant_of and frappe.db.get_value('Item', doc.variant_of, 'show_in_website'): + item_code = doc.variant_of + + if item_code: + item_cache = ItemVariantsCacheManager(item_code) + item_cache.clear_cache() + def check_stock_uom_with_bin(item, stock_uom): if stock_uom == frappe.db.get_value("Item", item, "stock_uom"): @@ -922,7 +973,7 @@ def check_stock_uom_with_bin(item, stock_uom): matched = True ref_uom = frappe.db.get_value("Stock Ledger Entry", - {"item_code": item}, "stock_uom") + {"item_code": item}, "stock_uom") if ref_uom: if cstr(ref_uom) != cstr(stock_uom): @@ -931,7 +982,7 @@ def check_stock_uom_with_bin(item, stock_uom): bin_list = frappe.db.sql("select * from tabBin where item_code=%s", item, as_dict=1) for bin in bin_list: if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 - or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom): + or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom): matched = False break diff --git a/erpnext/stock/doctype/item/test_records.json b/erpnext/stock/doctype/item/test_records.json index b09a3c0866..6c1a55945c 100644 --- a/erpnext/stock/doctype/item/test_records.json +++ b/erpnext/stock/doctype/item/test_records.json @@ -309,7 +309,8 @@ "warehouse_reorder_level": 20, "warehouse_reorder_qty": 20 } - ] + ], + "show_in_website": 1 }, { "description": "_Test Item 1", diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.js b/erpnext/stock/doctype/item_attribute/item_attribute.js new file mode 100644 index 0000000000..f253e22327 --- /dev/null +++ b/erpnext/stock/doctype/item_attribute/item_attribute.js @@ -0,0 +1,6 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Item Attribute', { + +}); diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.json b/erpnext/stock/doctype/item_attribute/item_attribute.json index 4b23cf0604..2fbff4e614 100644 --- a/erpnext/stock/doctype/item_attribute/item_attribute.json +++ b/erpnext/stock/doctype/item_attribute/item_attribute.json @@ -1,212 +1,294 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, "autoname": "field:attribute_name", + "beta": 0, "creation": "2014-09-26 03:49:54.899170", "custom": 0, "docstatus": 0, "doctype": "DocType", "document_type": "Setup", + "editable_grid": 0, "fields": [ { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "attribute_name", "fieldtype": "Data", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Attribute Name", "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": 1, "search_index": 0, "set_only_once": 0, - "unique": 0 + "translatable": 0, + "unique": 1 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "default": "0", "fieldname": "numeric_values", "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": "Numeric Values", "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": "numeric_values", "fieldname": "section_break_4", "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": "", "fieldname": "from_range", "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": "From Range", "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": "increment", "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": "Increment", "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": "column_break_8", "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, "depends_on": "", "fieldname": "to_range", "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": "To Range", "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.numeric_values", "fieldname": "section_break_5", "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": "", "fieldname": "item_attribute_values", "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": "Item Attribute Values", "length": 0, "no_copy": 0, @@ -214,24 +296,29 @@ "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 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-edit", + "idx": 0, + "image_view": 0, "in_create": 0, - "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2015-11-16 06:29:48.198647", + "modified": "2019-01-01 13:17:46.524806", "modified_by": "Administrator", "module": "Stock", "name": "Item Attribute", @@ -240,7 +327,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -259,8 +345,13 @@ "write": 1 } ], + "quick_entry": 0, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json index 1e55580a2c..6d02ea9db0 100644 --- a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json +++ b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, @@ -14,6 +15,40 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "variant_of", + "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": "Variant Of", + "length": 0, + "no_copy": 0, + "options": "Item", + "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, @@ -41,10 +76,12 @@ "reqd": 1, "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, @@ -70,10 +107,12 @@ "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, @@ -102,10 +141,12 @@ "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, @@ -133,10 +174,12 @@ "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, @@ -163,10 +206,12 @@ "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, @@ -194,10 +239,12 @@ "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, @@ -225,10 +272,12 @@ "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, @@ -254,10 +303,12 @@ "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, @@ -285,6 +336,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -299,7 +351,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-12-11 11:26:25.126350", + "modified": "2019-01-03 15:36:59.129006", "modified_by": "Administrator", "module": "Stock", "name": "Item Variant Attribute", @@ -313,5 +365,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 0, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/templates/generators/item.html b/erpnext/templates/generators/item.html deleted file mode 100644 index b258bde2eb..0000000000 --- a/erpnext/templates/generators/item.html +++ /dev/null @@ -1,143 +0,0 @@ -{% extends "templates/web.html" %} - -{% block title %} {{ title }} {% endblock %} - -{% block breadcrumbs %} - {% include "templates/includes/breadcrumbs.html" %} -{% endblock %} - -{% block page_content %} -{% from "erpnext/templates/includes/macros.html" import product_image %} -
-
-
-
- {% if slideshow %} - {% set slideshow_items = frappe.get_list(doctype="Website Slideshow Item", fields=["image"], filters={ "parent": doc.slideshow }) %} -
- {%- for slideshow_item in slideshow_items -%} - {% set image_src = slideshow_item['image'] %} - {% if image_src %} -
- -
- {% endif %} - {% endfor %} -
-
-
- {% set first_image = slideshow_items[0]['image'] %} - {{ product_image(first_image, "product-full-image") }} -
-
- {% else %} -
- {{ product_image(website_image, "product-full-image") }} -
- {% endif %} -
-

{{ item_name }}

-

- {{ _("Item Code") }}: {{ variant and variant.name or name }} -

-
-
- {% if has_variants and attributes %} - - {% for d in attributes %} - {% if attribute_values[d.attribute] -%} - - {%- endif %} - {% endfor %} - - {% endif %} -
-
-
-
-

-
-
-
-
- -
- - - - - - - -
-
-
-
- -
- -
-
-
-
-
-
-
{{ _("Description") }}
-
- {{ web_long_description or description or _("No description given") }} -
-
-
- {% if website_specifications -%} -
-
-
{{ _("Specifications") }}
- - - {% for d in website_specifications -%} - - - - - {%- endfor %} -
{{ d.label }}{{ d.description }}
-
-
- {%- endif %} -
-
-
- -{% endblock %} diff --git a/erpnext/templates/generators/item/item.html b/erpnext/templates/generators/item/item.html new file mode 100644 index 0000000000..d3691a6e99 --- /dev/null +++ b/erpnext/templates/generators/item/item.html @@ -0,0 +1,32 @@ +{% extends "templates/web.html" %} + +{% block title %} {{ title }} {% endblock %} + +{% block breadcrumbs %} + {% include "templates/includes/breadcrumbs.html" %} +{% endblock %} + +{% block page_content %} +{% from "erpnext/templates/includes/macros.html" import product_image %} +
+
+
+ {% include "templates/generators/item/item_image.html" %} + {% include "templates/generators/item/item_details.html" %} +
+ + {% include "templates/generators/item/item_specifications.html" %} + + {{ doc.website_content or '' }} +
+
+{% endblock %} + +{% block base_scripts %} + + + + + + +{% endblock %} \ No newline at end of file diff --git a/erpnext/templates/generators/item/item_add_to_cart.html b/erpnext/templates/generators/item/item_add_to_cart.html new file mode 100644 index 0000000000..f4a31a7e73 --- /dev/null +++ b/erpnext/templates/generators/item/item_add_to_cart.html @@ -0,0 +1,67 @@ +{% if shopping_cart and shopping_cart.cart_settings.enabled %} + +{% set cart_settings = shopping_cart.cart_settings %} +{% set product_info = shopping_cart.product_info %} + +
+
+ {% if cart_settings.show_price and product_info.price %} +

+ {{ product_info.price.formatted_price_sales_uom }} + ({{ product_info.price.formatted_price }} / {{ product_info.uom }}) +

+ {% endif %} + {% if cart_settings.show_stock_availability %} +
+ {% if product_info.in_stock == 0 %} + + {{ _('Not in stock') }} + + {% elif product_info.in_stock == 1 %} + + {{ _('In stock') }} + {% if product_info.show_stock_qty and product_info.stock_qty %} + ({{ product_info.stock_qty[0][0] }}) + {% endif %} + + {% endif %} +
+ {% endif %} +
+ + {{ _("View in Cart") }} + + +
+
+
+ + + +{% endif %} \ No newline at end of file diff --git a/erpnext/templates/generators/item/item_configure.html b/erpnext/templates/generators/item/item_configure.html new file mode 100644 index 0000000000..04f89eca9d --- /dev/null +++ b/erpnext/templates/generators/item/item_configure.html @@ -0,0 +1,23 @@ +{% if shopping_cart and shopping_cart.cart_settings.enabled %} +{% set cart_settings = shopping_cart.cart_settings %} + +
+ {% if cart_settings.show_configure_button | int %} + + {% endif %} + {% if cart_settings.show_contact_us_button | int %} + + {% endif %} +
+ +{% endif %} diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js new file mode 100644 index 0000000000..5fd901169f --- /dev/null +++ b/erpnext/templates/generators/item/item_configure.js @@ -0,0 +1,318 @@ +class ItemConfigure { + constructor(item_code, item_name) { + this.item_code = item_code; + this.item_name = item_name; + + this.get_attributes_and_values() + .then(attribute_data => { + this.attribute_data = attribute_data; + this.show_configure_dialog(); + }); + } + + show_configure_dialog() { + const fields = this.attribute_data.map(a => { + return { + fieldtype: 'Select', + label: a.attribute, + fieldname: a.attribute, + options: a.values.map(v => { + return { + label: v, + value: v + }; + }), + change: (e) => { + this.on_attribute_selection(e); + } + }; + }); + + this.dialog = new frappe.ui.Dialog({ + title: __('Configure {0}', [this.item_name]), + fields, + on_hide: () => { + set_continue_configuration(); + } + }); + + this.attribute_data.forEach(a => { + const field = this.dialog.get_field(a.attribute); + const $a = $(`${__("Clear")}`); + $a.on('click', (e) => { + e.preventDefault(); + this.dialog.set_value(a.attribute, ''); + }); + field.$wrapper.find('.help-box').append($a); + }); + + this.append_status_area(); + this.dialog.show(); + + this.dialog.set_values(JSON.parse(localStorage.getItem(this.get_cache_key()))); + + $('.btn-configure').prop('disabled', false); + } + + on_attribute_selection(e) { + if (e) { + const changed_fieldname = $(e.target).data('fieldname'); + this.show_range_input_if_applicable(changed_fieldname); + } else { + this.show_range_input_for_all_fields(); + } + + const values = this.dialog.get_values(); + if (Object.keys(values).length === 0) { + this.clear_status(); + localStorage.removeItem(this.get_cache_key()); + return; + } + + // save state + localStorage.setItem(this.get_cache_key(), JSON.stringify(values)); + + // show + this.set_loading_status(); + + this.get_next_attribute_and_values(values) + .then(data => { + const { + valid_options_for_attributes, + } = data; + + this.set_item_found_status(data); + + for (let attribute in valid_options_for_attributes) { + const valid_options = valid_options_for_attributes[attribute]; + const options = this.dialog.get_field(attribute).df.options; + const new_options = options.map(o => { + o.disabled = !valid_options.includes(o.value); + return o; + }); + + this.dialog.set_df_property(attribute, 'options', new_options); + this.dialog.get_field(attribute).set_options(); + } + }); + } + + show_range_input_for_all_fields() { + this.dialog.fields.forEach(f => { + this.show_range_input_if_applicable(f.fieldname); + }); + } + + show_range_input_if_applicable(fieldname) { + const changed_field = this.dialog.get_field(fieldname); + const changed_value = changed_field.get_value(); + if (changed_value && changed_value.includes(' to ')) { + // possible range input + let numbers = changed_value.split(' to '); + numbers = numbers.map(number => parseFloat(number)); + + if (!numbers.some(n => isNaN(n))) { + numbers.sort((a, b) => a - b); + if (changed_field.$input_wrapper.find('.range-selector').length) { + return; + } + const parent = $('
') + .insertBefore(changed_field.$input_wrapper.find('.help-box')); + const control = frappe.ui.form.make_control({ + df: { + fieldtype: 'Int', + label: __('Enter value betweeen {0} and {1}', [numbers[0], numbers[1]]), + change: () => { + const value = control.get_value(); + if (value < numbers[0] || value > numbers[1]) { + control.$wrapper.addClass('was-validated'); + control.set_description( + __('Value must be between {0} and {1}', [numbers[0], numbers[1]])); + control.$input[0].setCustomValidity('error'); + } else { + control.$wrapper.removeClass('was-validated'); + control.set_description(''); + control.$input[0].setCustomValidity(''); + this.update_range_values(fieldname, value); + } + } + }, + render_input: true, + parent + }); + control.$wrapper.addClass('mt-3'); + } + } + } + + update_range_values(attribute, range_value) { + this.range_values = this.range_values || {}; + this.range_values[attribute] = range_value; + } + + show_remaining_optional_attributes() { + // show all attributes if remaining + // unselected attributes are all optional + const unselected_attributes = this.dialog.fields.filter(df => { + const value_selected = this.dialog.get_value(df.fieldname); + return !value_selected; + }); + const is_optional_attribute = df => { + const optional_attributes = this.attribute_data + .filter(a => a.optional).map(a => a.attribute); + return optional_attributes.includes(df.fieldname); + }; + if (unselected_attributes.every(is_optional_attribute)) { + unselected_attributes.forEach(df => { + this.dialog.fields_dict[df.fieldname].$wrapper.show(); + }); + } + } + + set_loading_status() { + this.dialog.$status_area.html(` + + `); + } + + set_item_found_status(data) { + const html = this.get_html_for_item_found(data); + this.dialog.$status_area.html(html); + } + + clear_status() { + this.dialog.$status_area.empty(); + } + + get_html_for_item_found({ filtered_items_count, filtered_items, exact_match, product_info }) { + const exact_match_message = __('1 exact match.'); + const one_item = exact_match.length === 1 ? + exact_match[0] : + filtered_items_count === 1 ? + filtered_items[0] : ''; + + const item_add_to_cart = one_item ? ` + + `: ''; + + const items_found = filtered_items_count === 1 ? + __('{0} item found.', [filtered_items_count]) : + __('{0} items found.', [filtered_items_count]); + + const item_found_status = ` + + `; + + return ` + ${item_add_to_cart} + ${item_found_status} + `; + } + + btn_add_to_cart(e) { + if (frappe.session.user !== 'Guest') { + localStorage.removeItem(this.get_cache_key()); + } + const item_code = $(e.currentTarget).data('item-code'); + const additional_notes = Object.keys(this.range_values || {}).map(attribute => { + return `${attribute}: ${this.range_values[attribute]}`; + }).join('\n'); + erpnext.shopping_cart.update_cart({ + item_code, + additional_notes, + qty: 1 + }); + this.dialog.hide(); + } + + btn_clear_values() { + this.dialog.fields_list.forEach(f => { + f.df.options = f.df.options.map(option => { + option.disabled = false; + return option; + }); + }); + this.dialog.clear(); + this.on_attribute_selection(); + } + + append_status_area() { + this.dialog.$status_area = $('
'); + this.dialog.$wrapper.find('.modal-body').prepend(this.dialog.$status_area); + this.dialog.$wrapper.on('click', '[data-action]', (e) => { + e.preventDefault(); + const $target = $(e.currentTarget); + const action = $target.data('action'); + const method = this[action]; + method.call(this, e); + }); + this.dialog.$body.css({ maxHeight: '75vh', overflow: 'auto', overflowX: 'hidden' }); + } + + get_next_attribute_and_values(selected_attributes) { + return this.call('erpnext.portal.product_configurator.utils.get_next_attribute_and_values', { + item_code: this.item_code, + selected_attributes + }); + } + + get_attributes_and_values() { + return this.call('erpnext.portal.product_configurator.utils.get_attributes_and_values', { + item_code: this.item_code + }); + } + + get_cache_key() { + return `configure:${this.item_code}`; + } + + call(method, args) { + // promisified frappe.call + return new Promise((resolve, reject) => { + frappe.call(method, args) + .then(r => resolve(r.message)) + .fail(reject); + }); + } +} + +function set_continue_configuration() { + const $btn_configure = $('.btn-configure'); + const { itemCode } = $btn_configure.data(); + + if (localStorage.getItem(`configure:${itemCode}`)) { + $btn_configure.text(__('Continue Configuration')); + } else { + $btn_configure.text(__('Configure')); + } +} + +frappe.ready(() => { + const $btn_configure = $('.btn-configure'); + if (!$btn_configure.length) return; + const { itemCode, itemName } = $btn_configure.data(); + + set_continue_configuration(); + + $btn_configure.on('click', () => { + $btn_configure.prop('disabled', true); + new ItemConfigure(itemCode, itemName); + }); +}); diff --git a/erpnext/templates/generators/item/item_details.html b/erpnext/templates/generators/item/item_details.html new file mode 100644 index 0000000000..4f8f8c21a5 --- /dev/null +++ b/erpnext/templates/generators/item/item_details.html @@ -0,0 +1,22 @@ +
+ +

+ {{ item_name }} +

+

+ {{ _("Item Code") }}: + {{ doc.name }} +

+ +
+ {{ doc.web_long_description or doc.description or _("No description given") | safe }} +
+ +{% if has_variants %} + + {% include "templates/generators/item/item_configure.html" %} +{% else %} + + {% include "templates/generators/item/item_add_to_cart.html" %} +{% endif %} +
diff --git a/erpnext/templates/generators/item/item_image.html b/erpnext/templates/generators/item/item_image.html new file mode 100644 index 0000000000..0dd4c3505e --- /dev/null +++ b/erpnext/templates/generators/item/item_image.html @@ -0,0 +1,107 @@ +
+{% if slides %} +{{ product_image(slides[0].image, 'product-image') }} +
+ {% for item in slides %} + {{ item.heading }} + {% endfor %} +
+ + +{% else %} +{{ product_image(website_image or image or 'no-image.jpg') }} +{% endif %} + + + + +
+ + diff --git a/erpnext/templates/generators/item/item_inquiry.js b/erpnext/templates/generators/item/item_inquiry.js new file mode 100644 index 0000000000..52ddae2624 --- /dev/null +++ b/erpnext/templates/generators/item/item_inquiry.js @@ -0,0 +1,70 @@ +frappe.ready(() => { + const d = new frappe.ui.Dialog({ + title: __('Contact Us'), + fields: [ + { + fieldtype: 'Data', + label: __('Full Name'), + fieldname: 'lead_name', + reqd: 1 + }, + { + fieldtype: 'Data', + label: __('Organization Name'), + fieldname: 'company_name', + }, + { + fieldtype: 'Data', + label: __('Email'), + fieldname: 'email_id', + options: 'Email', + reqd: 1 + }, + { + fieldtype: 'Data', + label: __('Subject'), + fieldname: 'subject', + reqd: 1 + }, + { + fieldtype: 'Text', + label: __('Message'), + fieldname: 'message', + reqd: 1 + } + ], + primary_action: send_inquiry, + primary_action_label: __('Send') + }); + + function send_inquiry() { + const values = d.get_values(); + const doc = Object.assign({}, values); + delete doc.subject; + delete doc.message; + + d.hide(); + + frappe.call('erpnext.shopping_cart.cart.create_lead_for_item_inquiry', { + lead: doc, + subject: values.subject, + message: values.message + }).then(r => { + if (r.message) { + d.clear(); + } + }); + } + + $('.btn-inquiry').click((e) => { + const $btn = $(e.target); + const item_code = $btn.data('item-code'); + d.set_value('subject', 'Inquiry about ' + item_code); + if (!['Administrator', 'Guest'].includes(frappe.session.user)) { + d.set_value('email_id', frappe.session.user); + d.set_value('lead_name', frappe.get_cookie('full_name')); + } + + d.show(); + }); +}); \ No newline at end of file diff --git a/erpnext/templates/generators/item/item_specifications.html b/erpnext/templates/generators/item/item_specifications.html new file mode 100644 index 0000000000..a12a074fa9 --- /dev/null +++ b/erpnext/templates/generators/item/item_specifications.html @@ -0,0 +1,16 @@ +{% if doc.website_specifications -%} +
+
+
{{ _("Specifications") }}
+ + + {% for d in doc.website_specifications -%} + + + + + {%- endfor %} +
{{ d.label }}{{ d.description }}
+
+
+{%- endif %} \ No newline at end of file diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html index cf8aa15337..3f98453603 100644 --- a/erpnext/templates/generators/item_group.html +++ b/erpnext/templates/generators/item_group.html @@ -9,29 +9,32 @@ {% include "templates/includes/slideshow.html" %} {% endif %} {% if description %} -
{{ description or ""}}
+
{{ description or ""}}
{% endif %}
-
- {% if items %} -
- {% for i in range(0, page_length) %} - {% if items[i] %} - {{ items[i] }} +
+
+ {% if items %} +
+ {% for i in range(0, page_length) %} + {% if items[i] %} + {%- set item = items[i] %} + {% include "erpnext/www/all-products/item_row.html" %} + {% endif %} + {% endfor %} +
+
+ {% if frappe.form_dict.start|int > 0 %} + {{ _("Prev") }} {% endif %} - {% endfor %} -
-
- {% if frappe.form_dict.start|int > 0 %} - {{ _("Prev") }} - {% endif %} - {% if items|length > page_length %} - {{ _("Next") }} - {% endif %} -
- {% else %} + {% if items|length > page_length %} + {{ _("Next") }} + {% endif %} +
+ {% else %}
{{ _("No items listed") }}.
- {% endif %} + {% endif %} +
{% endblock %} \ No newline at end of file diff --git a/erpnext/templates/includes/address_row.html b/erpnext/templates/includes/address_row.html index bfc035a7e7..dadd2dff10 100644 --- a/erpnext/templates/includes/address_row.html +++ b/erpnext/templates/includes/address_row.html @@ -1,12 +1,12 @@ -
- +
+
-
+
{{ doc.address_title }}
-
{{ _(doc.address_type) }}
-
{{ doc.city }}
-
+
{{ _(doc.address_type) }}
+
{{ doc.city }}
+
{{ frappe.get_doc(doc).get_display() }}
diff --git a/erpnext/templates/includes/cart.js b/erpnext/templates/includes/cart.js index 51be9541ec..983898b457 100644 --- a/erpnext/templates/includes/cart.js +++ b/erpnext/templates/includes/cart.js @@ -16,41 +16,33 @@ $.extend(shopping_cart, { bind_events: function() { shopping_cart.bind_address_select(); shopping_cart.bind_place_order(); + shopping_cart.bind_request_quotation(); shopping_cart.bind_change_qty(); + shopping_cart.bind_change_notes(); shopping_cart.bind_dropdown_cart_buttons(); }, bind_address_select: function() { - $(".cart-addresses").find('input[data-address-name]').on("click", function() { - if($(this).prop("checked")) { - var me = this; + $(".cart-addresses").on('click', '.address-card', function(e) { + const $card = $(e.currentTarget); + const address_fieldname = $card.closest('[data-fieldname]').attr('data-fieldname'); + const address_name = $card.closest('[data-address-name]').attr('data-address-name'); - // uncheck other shipping or billing addresses: - if ( $(this).is('input[data-fieldname=customer_address]') ) { - $('input[data-fieldname=customer_address]').not(this).prop('checked', false); - } else { - $('input[data-fieldname=shipping_address_name]').not(this).prop('checked', false); - } - - return frappe.call({ - type: "POST", - method: "erpnext.shopping_cart.cart.update_cart_address", - freeze: true, - args: { - address_fieldname: $(this).attr("data-fieldname"), - address_name: $(this).attr("data-address-name") - }, - callback: function(r) { - if(!r.exc) { - $(".cart-tax-items").html(r.message.taxes); - } + return frappe.call({ + type: "POST", + method: "erpnext.shopping_cart.cart.update_cart_address", + freeze: true, + args: { + address_fieldname, + address_name + }, + callback: function(r) { + if(!r.exc) { + $(".cart-tax-items").html(r.message.taxes); } - }); - } else { - return false; - } + } + }); }); - }, bind_place_order: function() { @@ -59,12 +51,18 @@ $.extend(shopping_cart, { }); }, + bind_request_quotation: function() { + $('.btn-request-for-quotation').on('click', function() { + shopping_cart.request_quotation(this); + }); + }, + bind_change_qty: function() { // bind update button $(".cart-items").on("change", ".cart-qty", function() { var item_code = $(this).attr("data-item-code"); var newVal = $(this).val(); - shopping_cart.shopping_cart_update(item_code, newVal); + shopping_cart.shopping_cart_update({item_code, qty: newVal}); }); $(".cart-items").on('click', '.number-spinner button', function () { @@ -82,7 +80,21 @@ $.extend(shopping_cart, { } input.val(newVal); var item_code = input.attr("data-item-code"); - shopping_cart.shopping_cart_update(item_code, newVal); + shopping_cart.shopping_cart_update({item_code, qty: newVal}); + }); + }, + + bind_change_notes: function() { + $('.cart-items').on('change', 'textarea', function() { + const $textarea = $(this); + const item_code = $textarea.attr('data-item-code'); + const qty = $textarea.closest('tr').find('.cart-qty').val(); + const notes = $textarea.val(); + shopping_cart.shopping_cart_update({ + item_code, + qty, + additional_notes: notes + }); }); }, @@ -150,7 +162,32 @@ $.extend(shopping_cart, { .html(msg || frappe._("Something went wrong!")) .toggle(true); } else { - window.location.href = "/orders/" + encodeURIComponent(r.message); + window.open('/orders/' + encodeURIComponent(r.message), '_blank'); + window.location.reload(); + } + } + }); + }, + + request_quotation: function(btn) { + return frappe.call({ + type: "POST", + method: "erpnext.shopping_cart.cart.request_for_quotation", + btn: btn, + callback: function(r) { + if(r.exc) { + var msg = ""; + if(r._server_messages) { + msg = JSON.parse(r._server_messages || []).join("
"); + } + + $("#cart-error") + .empty() + .html(msg || frappe._("Something went wrong!")) + .toggle(true); + } else { + window.open('/printview?doctype=Quotation&name=' + r.message, '_blank'); + window.location.reload(); } } }); diff --git a/erpnext/templates/includes/cart/address_card.html b/erpnext/templates/includes/cart/address_card.html new file mode 100644 index 0000000000..c91723e91e --- /dev/null +++ b/erpnext/templates/includes/cart/address_card.html @@ -0,0 +1,12 @@ +
diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html index 7bd92562e2..2c90f8cb87 100644 --- a/erpnext/templates/includes/cart/cart_address.html +++ b/erpnext/templates/includes/cart/cart_address.html @@ -1,26 +1,141 @@ {% from "erpnext/templates/includes/cart/cart_macros.html" import show_address %} -
- {% if addresses|length == 1%} - {% set select_address = True %} - {% endif %} -
-
{{ _("Shipping Address") }}
-
- {% for address in shipping_addresses %} - {{ show_address(address, doc, "shipping_address_name", select_address) }} - {% endfor %} -
- - {{ _("Manage Addresses") }} -
-
-
{{ _("Billing Address") }}
-
- {% for address in billing_addresses %} - {{ show_address(address, doc, "customer_address", select_address) }} - {% endfor %} -
+ +{% if addresses | length == 1%} + {% set select_address = True %} +{% endif %} + +
+
{{ _("Shipping Address") }}
+
+ {% for address in shipping_addresses %} +
+ {% include "templates/includes/cart/address_card.html" %} +
+ {% endfor %}
+
+
{{ _("Billing Address") }}
+
+ {% for address in billing_addresses %} +
+ {% include "templates/includes/cart/address_card.html" %} +
+ {% endfor %} +
+
+
+ + +
+ + + diff --git a/erpnext/templates/includes/cart/cart_items.html b/erpnext/templates/includes/cart/cart_items.html index 65b81d9320..ca5744bebb 100644 --- a/erpnext/templates/includes/cart/cart_items.html +++ b/erpnext/templates/includes/cart/cart_items.html @@ -1,31 +1,42 @@ -{% from "erpnext/templates/includes/order/order_macros.html" import item_name_and_description %} -{% from "erpnext/templates/includes/order/order_macros.html" import item_name_and_description_cart %} - {% for d in doc.items %} -
-
- {{ item_name_and_description(d) }} -
-
- -
- - - - - - - -
+ + +
+ {{ d.item_name }} +
+
+ {{ d.item_code }} +
+ {%- set variant_of = frappe.db.get_value('Item', d.item_code, 'variant_of') %} + {% if variant_of %} + + {{ _('Variant of') }} {{ variant_of }} -
-
- {{ d.get_formatted("amount") }} -

{{ _("Rate") }} {{ d.get_formatted("rate") }}

-
-
-{% endfor %} \ No newline at end of file + {% endif %} +
+ +
+ + +
+ + + + + + + +
+ + {% if cart_settings.enable_checkout %} + +
+ {{ d.get_formatted('amount') }} +
+ + {{ _('Rate:') }} {{ d.get_formatted('rate') }} + + + {% endif %} + +{% endfor %} diff --git a/erpnext/templates/includes/footer/footer_extension.html b/erpnext/templates/includes/footer/footer_extension.html index 23a6a3464e..8cf3081dec 100644 --- a/erpnext/templates/includes/footer/footer_extension.html +++ b/erpnext/templates/includes/footer/footer_extension.html @@ -1,11 +1,14 @@ {% if not hide_footer_signup %} - + {% endif %} - + {% if cart_settings.enable_checkout %} +
+ {% include "templates/includes/cart/cart_address.html" %} +
+ {% endif %} + {% endif %} +
+ +
+
+ {% if cart_settings.enable_checkout %} + + {{ _('See past orders') }} + + {% else %} + + {{ _('See past quotations') }} + {% endif %}
- +{% endblock %} + +{% block base_scripts %} + + + + + + {% endblock %} diff --git a/erpnext/templates/pages/help.html b/erpnext/templates/pages/help.html index 8c26852e10..1cfe358efd 100644 --- a/erpnext/templates/pages/help.html +++ b/erpnext/templates/pages/help.html @@ -11,7 +11,7 @@ value='{{ frappe.form_dict.q or ''}}' {% if not frappe.form_dict.q%}placeholder="{{ _("What do you need help with?") }}"{% endif %}> + class='btn btn-sm btn-light btn-search' value="{{ _("Search") }}">
diff --git a/erpnext/templates/pages/home.css b/erpnext/templates/pages/home.css new file mode 100644 index 0000000000..cf5476635b --- /dev/null +++ b/erpnext/templates/pages/home.css @@ -0,0 +1,9 @@ +/* csslint ignore:start */ +{% if homepage.hero_image %} +.hero-image { + background-image: url("{{ homepage.hero_image }}"); + background-size: cover; + padding: 10rem 0; +} +{% endif %} +/* csslint ignore:end */ \ No newline at end of file diff --git a/erpnext/templates/pages/home.html b/erpnext/templates/pages/home.html index f36b4e0817..b67a4651db 100644 --- a/erpnext/templates/pages/home.html +++ b/erpnext/templates/pages/home.html @@ -1,75 +1,75 @@ {% extends "templates/web.html" %} -{% from "erpnext/templates/includes/macros.html" import product_image_square %} -{% block page_content %} +{% from "erpnext/templates/includes/macros.html" import render_homepage_section %} -
-
-
-

{{ homepage.tag_line or '' }}

-

{{ homepage.description or '' }}

+{% block content %} +
+ {% if homepage.hero_section_based_on == 'Default' %} +
+
+

{{ homepage.tag_line }}

+

{{ homepage.tag_line }}

+

{{ homepage.description }}

+

{{ homepage.description }}

- {% if homepage.products %} -
+ {% elif homepage.hero_section_based_on == 'Slideshow' and slideshow %} +
+ {% include "templates/includes/slideshow.html" %} +
+ {% elif homepage.hero_section_based_on == 'Homepage Section' %} + {{ render_homepage_section(homepage.hero_section_doc) }} + {% endif %} + + {% if homepage.products %} +
+

{{ _('Products') }}

+ +
+ {% for item in homepage.products %} +
+
+
+
+
{{ item.item_name }}
+ {{ _('More details') }} +
- + {% endfor %}
- {% endif %} -
-
-{% endblock %} + + {% endif %} -{% block style %} - -{% endblock %} + {% for section in homepage_sections %} + {{ render_homepage_section(section) }} + {% endfor %} + +{% endblock %} \ No newline at end of file diff --git a/erpnext/templates/pages/home.py b/erpnext/templates/pages/home.py index 82d525ac77..4b688b13df 100644 --- a/erpnext/templates/pages/home.py +++ b/erpnext/templates/pages/home.py @@ -15,15 +15,38 @@ def get_context(context): if route: item.route = '/' + route - context.title = homepage.title or homepage.company - - # show atleast 3 products - if len(homepage.products) < 3: - for i in range(3 - len(homepage.products)): - homepage.append('products', { - 'item_code': 'product-{0}'.format(i), - 'item_name': frappe._('Product {0}').format(i), - 'route': '#' - }) - + homepage.title = homepage.title or homepage.company + context.title = homepage.title context.homepage = homepage + + if homepage.hero_section_based_on == 'Homepage Section' and homepage.hero_section: + homepage.hero_section_doc = frappe.get_doc('Homepage Section', homepage.hero_section) + + if homepage.slideshow: + doc = frappe.get_doc('Website Slideshow', homepage.slideshow) + context.slideshow = homepage.slideshow + context.slideshow_header = doc.header + context.slides = doc.slideshow_items + + context.blogs = frappe.get_all('Blog Post', + fields=['title', 'blogger', 'blog_intro', 'route'], + filters={ + 'published': 1 + }, + order_by='modified desc', + limit=3 + ) + + # filter out homepage section which is used as hero section + homepage_hero_section = homepage.hero_section_based_on == 'Homepage Section' and homepage.hero_section + homepage_sections = frappe.get_all('Homepage Section', + filters=[['name', '!=', homepage_hero_section]] if homepage_hero_section else None, + order_by='section_order asc' + ) + context.homepage_sections = [frappe.get_doc('Homepage Section', name) for name in homepage_sections] + + context.metatags = context.metatags or frappe._dict({}) + context.metatags.image = homepage.hero_image or None + context.metatags.description = homepage.description or None + + context.explore_link = '/all-products' diff --git a/erpnext/templates/pages/material_request_info.html b/erpnext/templates/pages/material_request_info.html index ff3bd65b67..9d189895b6 100644 --- a/erpnext/templates/pages/material_request_info.html +++ b/erpnext/templates/pages/material_request_info.html @@ -12,7 +12,7 @@ {% endblock %} {% block header_actions %} -{{ _("Print") }} +{{ _("Print") }} {% endblock %} {% block page_content %} @@ -70,5 +70,5 @@ {% endif %} {% endfor %}
-
+
{% endblock %} \ No newline at end of file diff --git a/erpnext/templates/pages/non_profit/leave-chapter.html b/erpnext/templates/pages/non_profit/leave-chapter.html index 009c7af790..bc4242f919 100644 --- a/erpnext/templates/pages/non_profit/leave-chapter.html +++ b/erpnext/templates/pages/non_profit/leave-chapter.html @@ -9,7 +9,7 @@
-
diff --git a/erpnext/templates/pages/order.html b/erpnext/templates/pages/order.html index 64fd32a992..67a8fed8ab 100644 --- a/erpnext/templates/pages/order.html +++ b/erpnext/templates/pages/order.html @@ -8,23 +8,22 @@ {% block title %}{{ doc.name }}{% endblock %} {% block header %} -

{{ doc.name }}

+

{{ doc.name }}

{% endblock %} {% block header_actions %} -{{ _("Print") }} +{{ _("Print") }} {% endblock %} {% block page_content %}
-
- +
{{ _(doc.get('indicator_title')) or _(doc.status) or _("Submitted") }}
-
+
{{ frappe.utils.formatdate(doc.transaction_date, 'medium') }} {% if doc.valid_till %}

@@ -34,16 +33,14 @@

-

-{% if doc.doctype == 'Supplier Quotation' %} - {{ doc.supplier_name}} -{% else %} - {{ doc.customer_name}} -{% endif %} -{% if doc.contact_display %} -
- {{ doc.contact_display }} -{% endif %} +

+ {%- set party_name = doc.supplier_name if doc.doctype == 'Supplier Quotation' else doc.customer_name %} + {{ party_name }} + + {% if doc.contact_display and doc.contact_display != party_name %} +
+ {{ doc.contact_display }} + {% endif %}

{% if doc._header %} @@ -55,7 +52,7 @@
-
+
{{ _("Item") }}
@@ -67,7 +64,7 @@
{% for d in doc.items %}
-
+
{{ item_name_and_description(d) }}
@@ -85,11 +82,10 @@
-
-
-
+
+ {% include "erpnext/templates/includes/order/order_taxes.html" %} - +
@@ -115,7 +111,7 @@
- +

Available Points: {{ available_loyalty_points }}

{% endif %} diff --git a/erpnext/templates/pages/product_search.html b/erpnext/templates/pages/product_search.html index f9efd485d3..6a5425bbf8 100644 --- a/erpnext/templates/pages/product_search.html +++ b/erpnext/templates/pages/product_search.html @@ -25,7 +25,7 @@ frappe.ready(function() {
diff --git a/erpnext/templates/pages/projects.html b/erpnext/templates/pages/projects.html index baa2ae62cc..7e294e076b 100644 --- a/erpnext/templates/pages/projects.html +++ b/erpnext/templates/pages/projects.html @@ -20,11 +20,11 @@ aria-valuemin="0" aria-valuemax="100" style="width:{{ doc.percent_complete|round|int }}%;">
-{% endif %} +{% endif %}

diff --git a/erpnext/templates/pages/task_info.html b/erpnext/templates/pages/task_info.html index 6cfac28da6..6cd6a7e51a 100644 --- a/erpnext/templates/pages/task_info.html +++ b/erpnext/templates/pages/task_info.html @@ -20,7 +20,7 @@

- + {{ __("Cancel") }}
@@ -91,7 +91,7 @@ {% endfor %}
- {{ __("Add Comment") }} + {{ __("Add Comment") }}