From 4448e2d66bb224deee3a6dda181a358223d3c480 Mon Sep 17 00:00:00 2001 From: NahuelOperto Date: Fri, 21 Jun 2019 08:50:37 -0300 Subject: [PATCH 01/83] fix order in general ledger --- erpnext/accounts/report/general_ledger/general_ledger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 1c5e089534..2924907e8f 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -11,6 +11,7 @@ from erpnext.accounts.utils import get_account_currency from erpnext.accounts.report.financial_statements import get_cost_centers_with_children from six import iteritems from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from collections import OrderedDict def execute(filters=None): if not filters: @@ -267,7 +268,7 @@ def group_by_field(group_by): return 'voucher_no' def initialize_gle_map(gl_entries, filters): - gle_map = frappe._dict() + gle_map = OrderedDict() group_by = group_by_field(filters.get('group_by')) for gle in gl_entries: From 42fe68d06513a71d3f237510b32f69e29c634fdc Mon Sep 17 00:00:00 2001 From: NahuelOperto Date: Mon, 24 Jun 2019 08:29:14 -0300 Subject: [PATCH 02/83] feat: order type in trr type filter --- .../report/sales_analytics/sales_analytics.js | 2 +- .../report/sales_analytics/sales_analytics.py | 73 ++++++++++++++----- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js index fbe045bf35..149c923d5c 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.js +++ b/erpnext/selling/report/sales_analytics/sales_analytics.js @@ -8,7 +8,7 @@ frappe.query_reports["Sales Analytics"] = { fieldname: "tree_type", label: __("Tree Type"), fieldtype: "Select", - options: ["Customer Group","Customer","Item Group","Item","Territory"], + options: ["Customer Group","Customer","Item Group","Item","Territory","Order Type"], default: "Customer", reqd: 1 }, diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 3239fc626f..8a5e50a61e 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -23,15 +23,15 @@ class Analytics(object): self.get_columns() self.get_data() self.get_chart_data() - return self.columns, self.data , None, self.chart + return self.columns, self.data, None, self.chart def get_columns(self): - self.columns =[{ + self.columns = [{ "label": _(self.filters.tree_type + " ID"), - "options": self.filters.tree_type, + "options": self.filters.tree_type if self.filters.tree_type != "Order Type" else "", "fieldname": "entity", - "fieldtype": "Link", - "width": 140 + "fieldtype": "Link" if self.filters.tree_type != "Order Type" else "Data", + "width": 140 if self.filters.tree_type != "Order Type" else 200 }] if self.filters.tree_type in ["Customer", "Supplier", "Item"]: self.columns.append({ @@ -73,6 +73,28 @@ class Analytics(object): self.get_sales_transactions_based_on_item_group() self.get_rows_by_group() + elif self.filters.tree_type == "Order Type": + if self.filters.doc_type != "Sales Order": + self.data = [] + return + self.get_sales_transactions_based_on_order_type() + self.get_rows_by_group() + + def get_sales_transactions_based_on_order_type(self): + if self.filters["value_quantity"] == 'Value': + value_field = "base_net_total" + else: + value_field = "total_qty" + + self.entries = frappe.db.sql(""" select s.order_type as entity, s.{value_field} as value_field, s.{date_field} + from `tab{doctype}` s where s.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s + and ifnull(s.order_type, '') != '' order by s.order_type + """ + .format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type), + (self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1) + + self.get_teams() + def get_sales_transactions_based_on_customers_or_suppliers(self): if self.filters["value_quantity"] == 'Value': value_field = "base_net_total as value_field" @@ -88,7 +110,7 @@ class Analytics(object): self.entries = frappe.get_all(self.filters.doc_type, fields=[entity, entity_name, value_field, self.date_field], - filters = { + filters={ "docstatus": 1, "company": self.filters.company, self.date_field: ('between', [self.filters.from_date, self.filters.to_date]) @@ -112,7 +134,7 @@ class Analytics(object): where s.name = i.parent and i.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s """ - .format(date_field=self.date_field, value_field = value_field, doctype=self.filters.doc_type), + .format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type), (self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1) self.entity_names = {} @@ -135,7 +157,7 @@ class Analytics(object): self.entries = frappe.get_all(self.filters.doc_type, fields=[entity_field, value_field, self.date_field], - filters = { + filters={ "docstatus": 1, "company": self.filters.company, self.date_field: ('between', [self.filters.from_date, self.filters.to_date]) @@ -154,13 +176,13 @@ class Analytics(object): from `tab{doctype} Item` i , `tab{doctype}` s where s.name = i.parent and i.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s - """.format(date_field=self.date_field, value_field = value_field, doctype=self.filters.doc_type), + """.format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type), (self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1) self.get_groups() def get_rows(self): - self.data=[] + self.data = [] self.get_periodic_data() for entity, period_data in iteritems(self.entity_periodic_data): @@ -192,7 +214,7 @@ class Analytics(object): period = self.get_period(end_date) amount = flt(self.entity_periodic_data.get(d.name, {}).get(period, 0.0)) row[scrub(period)] = amount - if d.parent: + if d.parent and (self.filters.tree_type != "Order Type" or d.parent == "Order Types"): self.entity_periodic_data.setdefault(d.parent, frappe._dict()).setdefault(period, 0.0) self.entity_periodic_data[d.parent][period] += amount total += amount @@ -216,7 +238,7 @@ class Analytics(object): elif self.filters.range == 'Monthly': period = str(self.months[posting_date.month - 1]) + " " + str(posting_date.year) elif self.filters.range == 'Quarterly': - period = "Quarter " + str(((posting_date.month-1)//3)+1) +" " + str(posting_date.year) + period = "Quarter " + str(((posting_date.month - 1) // 3) + 1) + " " + str(posting_date.year) else: year = get_fiscal_year(posting_date, company=self.filters.company) period = str(year[0]) @@ -234,7 +256,7 @@ class Analytics(object): }.get(self.filters.range, 1) if self.filters.range in ['Monthly', 'Quarterly']: - from_date = from_date.replace(day = 1) + from_date = from_date.replace(day=1) elif self.filters.range == "Yearly": from_date = get_fiscal_year(from_date)[1] else: @@ -270,7 +292,22 @@ class Analytics(object): self.group_entries = frappe.db.sql("""select name, lft, rgt , {parent} as parent from `tab{tree}` order by lft""" - .format(tree=self.filters.tree_type, parent=parent), as_dict=1) + .format(tree=self.filters.tree_type, parent=parent), as_dict=1) + + for d in self.group_entries: + if d.parent: + self.depth_map.setdefault(d.name, self.depth_map.get(d.parent) + 1) + else: + self.depth_map.setdefault(d.name, 0) + + def get_teams(self): + self.depth_map = frappe._dict() + + self.group_entries = frappe.db.sql(""" select * from (select "Order Types" as name, 0 as lft, + 2 as rgt, '' as parent union select distinct order_type as name, 1 as lft, 1 as rgt, "Order Types" as parent + from `tab{doctype}` where ifnull(order_type, '') != '') as b order by lft, name + """ + .format(doctype=self.filters.doc_type), as_dict=1) for d in self.group_entries: if d.parent: @@ -285,13 +322,13 @@ class Analytics(object): length = len(self.columns) if self.filters.tree_type in ["Customer", "Supplier", "Item"]: - labels = [d.get("label") for d in self.columns[2:length-1]] + labels = [d.get("label") for d in self.columns[2:length - 1]] else: - labels = [d.get("label") for d in self.columns[1:length-1]] + labels = [d.get("label") for d in self.columns[1:length - 1]] self.chart = { "data": { 'labels': labels, - 'datasets':[] + 'datasets': [] }, "type": "line" - } \ No newline at end of file + } From 76afcf4cbac7c069fb60470db1d094283e0ed7de Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 3 Jul 2019 19:22:49 +0530 Subject: [PATCH 03/83] fix: Honor price list in Shopping Cart Settings --- erpnext/shopping_cart/cart.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 95bd9ba636..4019e07e4e 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -251,11 +251,13 @@ def _get_cart_quotation(party=None): if quotation: qdoc = frappe.get_doc("Quotation", quotation[0].name) else: + [company, price_list] = frappe.db.get_value("Shopping Cart Settings", None, ["company", "price_list"]) qdoc = frappe.get_doc({ "doctype": "Quotation", "naming_series": get_shopping_cart_settings().quotation_series or "QTN-CART-", "quotation_to": party.doctype, - "company": frappe.db.get_value("Shopping Cart Settings", None, "company"), + "company": company, + "selling_price_list": price_list, "order_type": "Shopping Cart", "status": "Draft", "docstatus": 0, From bdec7fea82bdce5ebdfe2a5080f79a76741341e4 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 4 Jul 2019 12:28:01 +0530 Subject: [PATCH 04/83] feat: purchase order against sales order (#17975) * feat: purchase order against sales order * fix: remove unwanted checks * fix:Related tests --- .../doctype/sales_order/sales_order.js | 76 +++++++++++++------ .../doctype/sales_order/sales_order.py | 7 +- .../doctype/sales_order/test_sales_order.py | 4 +- 3 files changed, 58 insertions(+), 29 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index f4bb0709a5..26ca7c6e22 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -107,7 +107,6 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( refresh: function(doc, dt, dn) { var me = this; this._super(); - var allow_purchase = false; var allow_delivery = false; if(doc.docstatus==1) { @@ -129,28 +128,9 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( me.frm.cscript.update_status('Re-open', 'Draft') }, __("Status")); } - } + } if(doc.status !== 'Closed') { if(doc.status !== 'On Hold') { - for (var i in this.frm.doc.items) { - var item = this.frm.doc.items[i]; - if(item.delivered_by_supplier === 1 || item.supplier){ - if(item.qty > flt(item.ordered_qty) - && item.qty > flt(item.delivered_qty)) { - allow_purchase = true; - } - } - - if (item.delivered_by_supplier===0) { - if(item.qty > flt(item.delivered_qty)) { - allow_delivery = true; - } - } - - if (allow_delivery && allow_purchase) { - break; - } - } if (this.frm.has_perm("submit")) { if(flt(doc.per_delivered, 6) < 100 || flt(doc.per_billed) < 100) { @@ -180,9 +160,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( } // make purchase order - if(flt(doc.per_delivered, 6) < 100 && allow_purchase) { this.frm.add_custom_button(__('Purchase Order'), () => this.make_purchase_order(), __('Create')); - } + // maintenance if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) { @@ -543,6 +522,42 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( filters: {'parent': me.frm.doc.name} } }}, + {fieldname: 'items_for_po', fieldtype: 'Table', label: 'Select Items', + fields: [ + { + fieldtype:'Data', + fieldname:'item_code', + label: __('Item'), + read_only:1, + in_list_view:1 + }, + { + fieldtype:'Data', + fieldname:'item_name', + label: __('Item name'), + read_only:1, + in_list_view:1 + }, + { + fieldtype:'Float', + fieldname:'qty', + label: __('Quantity'), + read_only: 1, + in_list_view:1 + }, + { + fieldtype:'Link', + read_only:1, + fieldname:'uom', + label: __('UOM'), + in_list_view:1 + } + ], + data: cur_frm.doc.items, + get_data: function() { + return cur_frm.doc.items + } + }, {"fieldtype": "Button", "label": __('Create Purchase Order'), "fieldname": "make_purchase_order", "cssClass": "btn-primary"}, ] @@ -550,13 +565,22 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( dialog.fields_dict.make_purchase_order.$input.click(function() { var args = dialog.get_values(); + let selected_items = dialog.fields_dict.items_for_po.grid.get_selected_children() + if(selected_items.length == 0) { + frappe.throw({message: 'Please select Item form Table', title: __('Message'), indicator:'blue'}) + } + let selected_items_list = [] + for(let i in selected_items){ + selected_items_list.push(selected_items[i].item_code) + } dialog.hide(); return frappe.call({ type: "GET", - method: "erpnext.selling.doctype.sales_order.sales_order.make_purchase_order_for_drop_shipment", + method: "erpnext.selling.doctype.sales_order.sales_order.make_purchase_order", args: { "source_name": me.frm.doc.name, - "for_supplier": args.supplier + "for_supplier": args.supplier, + "selected_items": selected_items_list }, freeze: true, callback: function(r) { @@ -576,6 +600,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( } }) }); + dialog.get_field("items_for_po").grid.only_sortable() + dialog.get_field("items_for_po").refresh() dialog.show(); }, hold_sales_order: function(){ diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 493da99303..8ad3bf0607 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -764,7 +764,10 @@ def get_events(start, end, filters=None): return data @frappe.whitelist() -def make_purchase_order_for_drop_shipment(source_name, for_supplier=None, target_doc=None): +def make_purchase_order(source_name, for_supplier=None, selected_items=[], target_doc=None): + if isinstance(selected_items, string_types): + selected_items = json.loads(selected_items) + def set_missing_values(source, target): target.supplier = supplier target.apply_discount_on = "" @@ -843,7 +846,7 @@ def make_purchase_order_for_drop_shipment(source_name, for_supplier=None, target "price_list_rate" ], "postprocess": update_item, - "condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier + "condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier and doc.item_code in selected_items } }, target_doc, set_missing_values) if not for_supplier: diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index e7697e2b0e..569c53f628 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -449,7 +449,7 @@ class TestSalesOrder(unittest.TestCase): frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1) def test_drop_shipping(self): - from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order_for_drop_shipment + from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order from erpnext.buying.doctype.purchase_order.purchase_order import update_status make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100) @@ -495,7 +495,7 @@ class TestSalesOrder(unittest.TestCase): so = make_sales_order(item_list=so_items, do_not_submit=True) so.submit() - po = make_purchase_order_for_drop_shipment(so.name, '_Test Supplier') + po = make_purchase_order(so.name, '_Test Supplier', selected_items=[so_items[0]['item_code']]) po.submit() dn = create_dn_against_so(so.name, delivered_qty=1) From 100af0a6356316a322bc8a48cde8aeda152ad479 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 4 Jul 2019 12:29:26 +0530 Subject: [PATCH 05/83] fix(salary-slip): Nonetype error on amount calculation (#18147) * fix: amount calculation * Update salary_slip.py --- erpnext/hr/doctype/salary_slip/salary_slip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index db0a3a5ece..6d25c06393 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -618,7 +618,7 @@ class SalarySlip(TransactionBase): elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days): amount, additional_amount = 0, 0 elif not row.amount: - amount = row.default_amount + row.additional_amount + amount = flt(row.default_amount) + flt(row.additional_amount) # apply rounding if frappe.get_cached_value("Salary Component", row.salary_component, "round_to_the_nearest_integer"): From 29734dae181279c8d478d7c58dab291e81c69abf Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 4 Jul 2019 12:50:16 +0530 Subject: [PATCH 06/83] patch: reloads newly created docs in v12 (#18148) --- erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py b/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py index 9925b70a96..3d4a9952c7 100644 --- a/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py +++ b/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py @@ -3,6 +3,8 @@ import frappe from erpnext.regional.india.setup import make_custom_fields def execute(): + frappe.reload_doc("accounts", "doctype", "tax_category") + frappe.reload_doc("stock", "doctype", "item_manufacturer") company = frappe.get_all('Company', filters = {'country': 'India'}) if not company: return From a9ea49c9767c50bd5c69d6fbc8a04a47a3bc12bc Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 4 Jul 2019 15:56:34 +0530 Subject: [PATCH 07/83] fix: Additonal discount not get set in sales transactions --- erpnext/selling/sales_common.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 9bae58b309..ad3f27c9c1 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -145,6 +145,11 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ }, discount_amount: function(doc, cdt, cdn) { + + if(doc.name === cdn) { + return; + } + var item = frappe.get_doc(cdt, cdn); item.discount_percentage = 0.0; this.apply_discount_on_item(doc, cdt, cdn, 'discount_amount'); From 95054a5a02ba19c70841ab2da5d151daa281d452 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 4 Jul 2019 16:13:56 +0530 Subject: [PATCH 08/83] fix: Try Faster Travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a8a0d82614..4a297e11c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ install: - nvm install 10 - pip install python-coveralls - wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py - - sudo python install.py --develop --user travis --without-bench-setup + - sudo python install.py --develop --user travis --without-bench-setup --frappe-branch-url https://github.com/adityahase/frappe --frappe-branch faster-travis - sudo pip install -e ~/bench - rm $TRAVIS_BUILD_DIR/.git/shallow From 697f995b27b4717823c13521e4a534ae04531783 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 4 Jul 2019 16:19:22 +0530 Subject: [PATCH 09/83] fix: Send --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4a297e11c0..200668d79b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ install: - nvm install 10 - pip install python-coveralls - wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py - - sudo python install.py --develop --user travis --without-bench-setup --frappe-branch-url https://github.com/adityahase/frappe --frappe-branch faster-travis + - sudo python install.py --develop --user travis --without-bench-setup --frappe-repo-url https://github.com/adityahase/frappe --frappe-branch faster-travis - sudo pip install -e ~/bench - rm $TRAVIS_BUILD_DIR/.git/shallow From d7291dbcad7dd9036eaad279ba6a30815a7486e6 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 4 Jul 2019 16:51:53 +0530 Subject: [PATCH 10/83] Revert "fix: Send" This reverts commit 697f995b27b4717823c13521e4a534ae04531783. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 200668d79b..4a297e11c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ install: - nvm install 10 - pip install python-coveralls - wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py - - sudo python install.py --develop --user travis --without-bench-setup --frappe-repo-url https://github.com/adityahase/frappe --frappe-branch faster-travis + - sudo python install.py --develop --user travis --without-bench-setup --frappe-branch-url https://github.com/adityahase/frappe --frappe-branch faster-travis - sudo pip install -e ~/bench - rm $TRAVIS_BUILD_DIR/.git/shallow From e5dc834ddb1f312a4d9514df6af9ddcffa0ad9a4 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 4 Jul 2019 16:52:01 +0530 Subject: [PATCH 11/83] Revert "fix: Try Faster Travis" This reverts commit 95054a5a02ba19c70841ab2da5d151daa281d452. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4a297e11c0..a8a0d82614 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ install: - nvm install 10 - pip install python-coveralls - wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py - - sudo python install.py --develop --user travis --without-bench-setup --frappe-branch-url https://github.com/adityahase/frappe --frappe-branch faster-travis + - sudo python install.py --develop --user travis --without-bench-setup - sudo pip install -e ~/bench - rm $TRAVIS_BUILD_DIR/.git/shallow From a5d975d867a0f09cdf97d93b2a579658bde1717b Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 4 Jul 2019 17:47:28 +0530 Subject: [PATCH 12/83] fix: Don't show Send SMS in BOM --- erpnext/public/js/controllers/transaction.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index bbd1f1c17b..741f21e712 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -383,8 +383,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ setup_sms: function() { var me = this; + let blacklist = ['Purchase Invoice', 'BOM']; if(this.frm.doc.docstatus===1 && !in_list(["Lost", "Stopped", "Closed"], this.frm.doc.status) - && this.frm.doctype != "Purchase Invoice") { + && !blacklist.includes(this.frm.doctype)) { this.frm.page.add_menu_item(__('Send SMS'), function() { me.send_sms(); }); } }, From 1d3008750c6de8a83b5ecbde0bb261ebde3fc8c4 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 4 Jul 2019 17:53:10 +0530 Subject: [PATCH 13/83] fix(UX): Move Bill of Materials section to first --- erpnext/config/manufacturing.py | 74 ++++++++++++++++----------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/erpnext/config/manufacturing.py b/erpnext/config/manufacturing.py index da60550221..c79c5b8b11 100644 --- a/erpnext/config/manufacturing.py +++ b/erpnext/config/manufacturing.py @@ -3,43 +3,6 @@ from frappe import _ def get_data(): return [ - { - "label": _("Production"), - "icon": "fa fa-star", - "items": [ - { - "type": "doctype", - "name": "Work Order", - "description": _("Orders released for production."), - "onboard": 1, - "dependencies": ["Item", "BOM"] - }, - { - "type": "doctype", - "name": "Production Plan", - "description": _("Generate Material Requests (MRP) and Work Orders."), - "onboard": 1, - "dependencies": ["Item", "BOM"] - }, - { - "type": "doctype", - "name": "Stock Entry", - "onboard": 1, - "dependencies": ["Item"] - }, - { - "type": "doctype", - "name": "Timesheet", - "description": _("Time Sheet for manufacturing."), - "onboard": 1, - "dependencies": ["Activity Type"] - }, - { - "type": "doctype", - "name": "Job Card" - } - ] - }, { "label": _("Bill of Materials"), "items": [ @@ -85,6 +48,43 @@ def get_data(): ] }, + { + "label": _("Production"), + "icon": "fa fa-star", + "items": [ + { + "type": "doctype", + "name": "Work Order", + "description": _("Orders released for production."), + "onboard": 1, + "dependencies": ["Item", "BOM"] + }, + { + "type": "doctype", + "name": "Production Plan", + "description": _("Generate Material Requests (MRP) and Work Orders."), + "onboard": 1, + "dependencies": ["Item", "BOM"] + }, + { + "type": "doctype", + "name": "Stock Entry", + "onboard": 1, + "dependencies": ["Item"] + }, + { + "type": "doctype", + "name": "Timesheet", + "description": _("Time Sheet for manufacturing."), + "onboard": 1, + "dependencies": ["Activity Type"] + }, + { + "type": "doctype", + "name": "Job Card" + } + ] + }, { "label": _("Tools"), "icon": "fa fa-wrench", From 2b969a469a05c2e4962cc080e71a6b6acf716616 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 4 Jul 2019 18:52:32 +0530 Subject: [PATCH 14/83] fix: Build item cache in long worker (#18165) --- erpnext/portal/product_configurator/item_variants_cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/portal/product_configurator/item_variants_cache.py b/erpnext/portal/product_configurator/item_variants_cache.py index f33c8d605b..fc294ce58b 100644 --- a/erpnext/portal/product_configurator/item_variants_cache.py +++ b/erpnext/portal/product_configurator/item_variants_cache.py @@ -110,4 +110,4 @@ def build_cache(item_code): 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') + frappe.enqueue(build_cache, item_code=item_code, queue='long') From 23c80658bae91468f18b6305430ed328ad582f5f Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 4 Jul 2019 18:54:04 +0530 Subject: [PATCH 15/83] fix: Add links in validation message --- erpnext/stock/doctype/stock_entry/stock_entry.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 464101e672..0abcbb328a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -324,8 +324,10 @@ class StockEntry(StockController): completed_qty = d.completed_qty + (allowance_percentage/100 * d.completed_qty) if total_completed_qty > flt(completed_qty): job_card = frappe.db.get_value('Job Card', {'operation_id': d.name}, 'name') - frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order # {3}. Please update operation status via Job Card # {4}") - .format(d.idx, d.operation, total_completed_qty, self.work_order, job_card), OperationsNotCompleteError) + work_order_link = frappe.utils.get_link_to_form('Work Order', self.work_order) + job_card_link = frappe.utils.get_link_to_form('Job Card', job_card) + frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.") + .format(d.idx, frappe.bold(d.operation), frappe.bold(total_completed_qty), work_order_link, job_card_link), OperationsNotCompleteError) def check_duplicate_entry_for_work_order(self): other_ste = [t[0] for t in frappe.db.get_values("Stock Entry", { From 7fc6021ca5ec621232354666477dc95e40c12626 Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Thu, 4 Jul 2019 22:46:16 +0530 Subject: [PATCH 16/83] feat(setup): adding selling buying filter in terms and conditions --- .../doctype/pos_profile/pos_profile.js | 4 + erpnext/hr/doctype/job_offer/job_offer.js | 6 + .../doctype/blanket_order/blanket_order.js | 23 + erpnext/patches.txt | 1 + ...default_buying_selling_terms_in_company.py | 19 + erpnext/public/js/controllers/buying.js | 8 + erpnext/public/js/controllers/transaction.js | 13 +- erpnext/selling/sales_common.js | 6 + erpnext/setup/doctype/company/company.js | 8 + erpnext/setup/doctype/company/company.json | 1543 +++++++++-------- .../terms_and_conditions.json | 246 +-- .../terms_and_conditions.py | 4 + erpnext/startup/boot.py | 2 +- 13 files changed, 916 insertions(+), 967 deletions(-) create mode 100644 erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js index 10f127a53f..5e94118d60 100755 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.js +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js @@ -8,6 +8,10 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) { return { filters: { selling: 1 } }; }); + frm.set_query("tc_name", function() { + return { filters: { selling: 1 } }; + }); + erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); }); diff --git a/erpnext/hr/doctype/job_offer/job_offer.js b/erpnext/hr/doctype/job_offer/job_offer.js index 1ee35afe10..c3d83c48cc 100755 --- a/erpnext/hr/doctype/job_offer/job_offer.js +++ b/erpnext/hr/doctype/job_offer/job_offer.js @@ -4,6 +4,12 @@ frappe.provide("erpnext.job_offer"); frappe.ui.form.on("Job Offer", { + onload: function (frm) { + frm.set_query("select_terms", function() { + return { filters: { hr: 1 } }; + }); + }, + select_terms: function (frm) { erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function (r) { if (!r.exc) { diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js index 89efb9369d..d1bef3fcd0 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js @@ -2,6 +2,10 @@ // For license information, please see license.txt frappe.ui.form.on('Blanket Order', { + onload: function(frm) { + frm.trigger('set_tc_name_filter'); + }, + setup: function(frm) { frm.add_fetch("customer", "customer_name", "customer_name"); frm.add_fetch("supplier", "supplier_name", "supplier_name"); @@ -44,4 +48,23 @@ frappe.ui.form.on('Blanket Order', { } }); }, + + set_tc_name_filter: function(frm) { + if (frm.doc.blanket_order_type === 'Selling'){ + frm.set_query("tc_name", function() { + return { filters: { selling: 1 } }; + }); + } + if (frm.doc.blanket_order_type === 'Purchasing'){ + frm.set_query("tc_name", function() { + return { filters: { buying: 1 } }; + }); + } + }, + + blanket_order_type: function (frm) { + frm.trigger('set_tc_name_filter'); + } }); + + diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 72db8ad063..bd41c7e884 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -615,3 +615,4 @@ erpnext.patches.v11_1.set_missing_opportunity_from erpnext.patches.v12_0.set_quotation_status erpnext.patches.v12_0.set_priority_for_support erpnext.patches.v12_0.delete_priority_property_setter +erpnext.patches.v12_0.add_default_buying_selling_terms_in_company diff --git a/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py b/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py new file mode 100644 index 0000000000..484f81a7ac --- /dev/null +++ b/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py @@ -0,0 +1,19 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe +from frappe.model.utils.rename_field import rename_field + +def execute(): + frappe.reload_doc("setup", "doctype", "company") + if frappe.db.has_column('Company', 'default_terms'): + rename_field('Company', "default_terms", "default_selling_terms") + + for company in frappe.get_all("Company", ["name", "default_selling_terms", "default_buying_terms"]): + if company.default_selling_terms and not company.default_buying_terms: + frappe.db.set_value("Company", company.name, "default_buying_terms", company.default_selling_terms) + + frappe.reload_doc("setup", "doctype", "terms_and_conditions") + frappe.db.sql("update `tabTerms and Conditions` set selling=1, buying=1, hr=1") diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 97c823d053..824b8d98d2 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -61,6 +61,14 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ }); } + if(this.frm.fields_dict.tc_name) { + this.frm.set_query("tc_name", function() { + return{ + filters: { 'buying': 1 } + } + }); + } + me.frm.set_query('supplier', erpnext.queries.supplier); me.frm.set_query('contact_person', erpnext.queries.contact_query); me.frm.set_query('supplier_address', erpnext.queries.address_query); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index a3ecea168d..11fdb8b178 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -585,8 +585,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ me.frm.set_value("letter_head", company_doc.default_letter_head); } } - if (company_doc.default_terms && me.frm.doc.doctype != "Purchase Invoice" && frappe.meta.has_field(me.frm.doc.doctype, "tc_name")) { - me.frm.set_value("tc_name", company_doc.default_terms); + let selling_doctypes_for_tc = ["Sales Invoice", "Quotation", "Sales Order", "Delivery Note"]; + if (company_doc.default_selling_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && + selling_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) { + me.frm.set_value("tc_name", company_doc.default_selling_terms); + } + let buying_doctypes_for_tc = ["Request for Quotation", "Supplier Quotation", "Purchase Order", + "Material Request", "Purchase Receipt"]; + // Purchase Invoice is excluded as per issue #3345 + if (company_doc.default_buying_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && + buying_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) { + me.frm.set_value("tc_name", company_doc.default_buying_terms); } frappe.run_serially([ diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 9bae58b309..00efb4f219 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -59,6 +59,12 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ }); } + if(this.frm.fields_dict.tc_name) { + this.frm.set_query("tc_name", function() { + return { filters: { selling: 1 } }; + }); + } + if(!this.frm.fields_dict["items"]) { return; } diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 1e6056ec86..313de677fc 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -17,6 +17,14 @@ frappe.ui.form.on("Company", { filters: {"is_group": 1} } }); + + frm.set_query("default_selling_terms", function() { + return { filters: { selling: 1 } }; + }); + + frm.set_query("default_buying_terms", function() { + return { filters: { buying: 1 } }; + }); }, company_name: function(frm) { diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index bb652ca6bf..bc3418997d 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -1,769 +1,776 @@ { - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:company_name", - "creation": "2013-04-10 08:35:39", - "description": "Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Organization.", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "details", - "company_name", - "abbr", - "change_abbr", - "is_group", - "cb0", - "domain", - "parent_company", - "charts_section", - "default_currency", - "default_letter_head", - "default_holiday_list", - "default_finance_book", - "standard_working_hours", - "default_terms", - "default_warehouse_for_sales_return", - "column_break_10", - "country", - "create_chart_of_accounts_based_on", - "chart_of_accounts", - "existing_company", - "tax_id", - "date_of_establishment", - "sales_settings", - "monthly_sales_target", - "sales_monthly_history", - "column_break_goals", - "transactions_annual_history", - "total_monthly_sales", - "default_settings", - "default_bank_account", - "default_cash_account", - "default_receivable_account", - "round_off_account", - "round_off_cost_center", - "write_off_account", - "discount_allowed_account", - "discount_received_account", - "exchange_gain_loss_account", - "unrealized_exchange_gain_loss_account", - "column_break0", - "allow_account_creation_against_child_company", - "default_payable_account", - "default_employee_advance_account", - "default_expense_account", - "default_income_account", - "default_deferred_revenue_account", - "default_deferred_expense_account", - "default_payroll_payable_account", - "default_expense_claim_payable_account", - "section_break_22", - "cost_center", - "column_break_26", - "credit_limit", - "payment_terms", - "auto_accounting_for_stock_settings", - "enable_perpetual_inventory", - "default_inventory_account", - "stock_adjustment_account", - "column_break_32", - "stock_received_but_not_billed", - "expenses_included_in_valuation", - "fixed_asset_depreciation_settings", - "accumulated_depreciation_account", - "depreciation_expense_account", - "series_for_depreciation_entry", - "expenses_included_in_asset_valuation", - "column_break_40", - "disposal_account", - "depreciation_cost_center", - "capital_work_in_progress_account", - "asset_received_but_not_billed", - "budget_detail", - "exception_budget_approver_role", - "company_info", - "company_logo", - "date_of_incorporation", - "address_html", - "date_of_commencement", - "phone_no", - "fax", - "email", - "website", - "column_break1", - "company_description", - "registration_info", - "registration_details", - "delete_company_transactions", - "lft", - "rgt", - "old_parent" - ], - "fields": [ - { - "fieldname": "details", - "fieldtype": "Section Break", - "oldfieldtype": "Section Break" - }, - { - "fieldname": "company_name", - "fieldtype": "Data", - "label": "Company", - "oldfieldname": "company_name", - "oldfieldtype": "Data", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "abbr", - "fieldtype": "Data", - "label": "Abbr", - "oldfieldname": "abbr", - "oldfieldtype": "Data", - "reqd": 1 - }, - { - "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", - "fieldname": "change_abbr", - "fieldtype": "Button", - "label": "Change Abbreviation" - }, - { - "bold": 1, - "default": "0", - "fieldname": "is_group", - "fieldtype": "Check", - "label": "Is Group" - }, - { - "fieldname": "default_finance_book", - "fieldtype": "Link", - "label": "Default Finance Book", - "options": "Finance Book" - }, - { - "fieldname": "cb0", - "fieldtype": "Column Break" - }, - { - "fieldname": "domain", - "fieldtype": "Link", - "label": "Domain", - "options": "Domain" - }, - { - "fieldname": "parent_company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Parent Company", - "options": "Company" - }, - { - "fieldname": "company_logo", - "fieldtype": "Attach Image", - "hidden": 1, - "label": "Company Logo" - }, - { - "fieldname": "company_description", - "fieldtype": "Text Editor", - "label": "Company Description" - }, - { - "collapsible": 1, - "fieldname": "sales_settings", - "fieldtype": "Section Break", - "label": "Sales Settings" - }, - { - "fieldname": "sales_monthly_history", - "fieldtype": "Small Text", - "hidden": 1, - "label": "Sales Monthly History", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "transactions_annual_history", - "fieldtype": "Code", - "hidden": 1, - "label": "Transactions Annual History", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "monthly_sales_target", - "fieldtype": "Currency", - "label": "Monthly Sales Target", - "options": "default_currency" - }, - { - "fieldname": "column_break_goals", - "fieldtype": "Column Break" - }, - { - "fieldname": "total_monthly_sales", - "fieldtype": "Currency", - "label": "Total Monthly Sales", - "no_copy": 1, - "options": "default_currency", - "read_only": 1 - }, - { - "fieldname": "charts_section", - "fieldtype": "Section Break", - "label": "Default Values" - }, - { - "fieldname": "default_currency", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Currency", - "options": "Currency", - "reqd": 1 - }, - { - "fieldname": "default_letter_head", - "fieldtype": "Link", - "label": "Default Letter Head", - "options": "Letter Head" - }, - { - "fieldname": "default_holiday_list", - "fieldtype": "Link", - "label": "Default Holiday List", - "options": "Holiday List" - }, - { - "fieldname": "standard_working_hours", - "fieldtype": "Float", - "label": "Standard Working Hours" - }, - { - "fieldname": "default_terms", - "fieldtype": "Link", - "label": "Default Terms", - "options": "Terms and Conditions" - }, - { - "fieldname": "default_warehouse_for_sales_return", - "fieldtype": "Link", - "label": "Default warehouse for Sales Return", - "options": "Warehouse" - }, - { - "fieldname": "column_break_10", - "fieldtype": "Column Break" - }, - { - "fieldname": "country", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Country", - "options": "Country", - "reqd": 1 - }, - { - "fieldname": "create_chart_of_accounts_based_on", - "fieldtype": "Select", - "label": "Create Chart Of Accounts Based On", - "options": "\nStandard Template\nExisting Company" - }, - { - "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"", - "fieldname": "chart_of_accounts", - "fieldtype": "Select", - "label": "Chart Of Accounts Template", - "no_copy": 1 - }, - { - "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"", - "fieldname": "existing_company", - "fieldtype": "Link", - "label": "Existing Company ", - "no_copy": 1, - "options": "Company" - }, - { - "fieldname": "tax_id", - "fieldtype": "Data", - "label": "Tax ID" - }, - { - "fieldname": "date_of_establishment", - "fieldtype": "Date", - "label": "Date of Establishment" - }, - { - "fieldname": "default_settings", - "fieldtype": "Section Break", - "label": "Accounts Settings", - "oldfieldtype": "Section Break" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_bank_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Bank Account", - "no_copy": 1, - "oldfieldname": "default_bank_account", - "oldfieldtype": "Link", - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_cash_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Cash Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_receivable_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Receivable Account", - "no_copy": 1, - "oldfieldname": "receivables_group", - "oldfieldtype": "Link", - "options": "Account" - }, - { - "fieldname": "round_off_account", - "fieldtype": "Link", - "label": "Round Off Account", - "options": "Account" - }, - { - "fieldname": "round_off_cost_center", - "fieldtype": "Link", - "label": "Round Off Cost Center", - "options": "Cost Center" - }, - { - "fieldname": "write_off_account", - "fieldtype": "Link", - "label": "Write Off Account", - "options": "Account" - }, - { - "fieldname": "discount_allowed_account", - "fieldtype": "Link", - "label": "Discount Allowed Account", - "options": "Account" - }, - { - "fieldname": "discount_received_account", - "fieldtype": "Link", - "label": "Discount Received Account", - "options": "Account" - }, - { - "fieldname": "exchange_gain_loss_account", - "fieldtype": "Link", - "label": "Exchange Gain / Loss Account", - "options": "Account" - }, - { - "fieldname": "unrealized_exchange_gain_loss_account", - "fieldtype": "Link", - "label": "Unrealized Exchange Gain/Loss Account", - "options": "Account" - }, - { - "fieldname": "column_break0", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, - { - "default": "0", - "depends_on": "eval:doc.parent_company", - "fieldname": "allow_account_creation_against_child_company", - "fieldtype": "Check", - "label": "Allow Account Creation Against Child Company" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_payable_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Payable Account", - "no_copy": 1, - "oldfieldname": "payables_group", - "oldfieldtype": "Link", - "options": "Account" - }, - { - "fieldname": "default_employee_advance_account", - "fieldtype": "Link", - "label": "Default Employee Advance Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_expense_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Cost of Goods Sold Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_income_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Income Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_deferred_revenue_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Deferred Revenue Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_deferred_expense_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Deferred Expense Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_payroll_payable_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Payroll Payable Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_expense_claim_payable_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Expense Claim Payable Account", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "section_break_22", - "fieldtype": "Section Break" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "cost_center", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Cost Center", - "no_copy": 1, - "options": "Cost Center" - }, - { - "fieldname": "column_break_26", - "fieldtype": "Column Break" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "credit_limit", - "fieldtype": "Currency", - "label": "Credit Limit", - "oldfieldname": "credit_limit", - "oldfieldtype": "Currency", - "options": "default_currency" - }, - { - "fieldname": "payment_terms", - "fieldtype": "Link", - "label": "Default Payment Terms Template", - "options": "Payment Terms Template" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "auto_accounting_for_stock_settings", - "fieldtype": "Section Break", - "label": "Stock Settings" - }, - { - "default": "1", - "fieldname": "enable_perpetual_inventory", - "fieldtype": "Check", - "label": "Enable Perpetual Inventory" - }, - { - "fieldname": "default_inventory_account", - "fieldtype": "Link", - "label": "Default Inventory Account", - "options": "Account" - }, - { - "fieldname": "stock_adjustment_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Stock Adjustment Account", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "column_break_32", - "fieldtype": "Column Break" - }, - { - "fieldname": "stock_received_but_not_billed", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Stock Received But Not Billed", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "expenses_included_in_valuation", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Expenses Included In Valuation", - "no_copy": 1, - "options": "Account" - }, - { - "collapsible": 1, - "fieldname": "fixed_asset_depreciation_settings", - "fieldtype": "Section Break", - "label": "Fixed Asset Depreciation Settings" - }, - { - "fieldname": "accumulated_depreciation_account", - "fieldtype": "Link", - "label": "Accumulated Depreciation Account", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "depreciation_expense_account", - "fieldtype": "Link", - "label": "Depreciation Expense Account", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "series_for_depreciation_entry", - "fieldtype": "Data", - "label": "Series for Asset Depreciation Entry (Journal Entry)" - }, - { - "fieldname": "expenses_included_in_asset_valuation", - "fieldtype": "Link", - "label": "Expenses Included In Asset Valuation", - "options": "Account" - }, - { - "fieldname": "column_break_40", - "fieldtype": "Column Break" - }, - { - "fieldname": "disposal_account", - "fieldtype": "Link", - "label": "Gain/Loss Account on Asset Disposal", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "depreciation_cost_center", - "fieldtype": "Link", - "label": "Asset Depreciation Cost Center", - "no_copy": 1, - "options": "Cost Center" - }, - { - "fieldname": "capital_work_in_progress_account", - "fieldtype": "Link", - "label": "Capital Work In Progress Account", - "options": "Account" - }, - { - "fieldname": "asset_received_but_not_billed", - "fieldtype": "Link", - "label": "Asset Received But Not Billed", - "options": "Account" - }, - { - "collapsible": 1, - "fieldname": "budget_detail", - "fieldtype": "Section Break", - "label": "Budget Detail" - }, - { - "fieldname": "exception_budget_approver_role", - "fieldtype": "Link", - "label": "Exception Budget Approver Role", - "options": "Role" - }, - { - "collapsible": 1, - "description": "For reference only.", - "fieldname": "company_info", - "fieldtype": "Section Break", - "label": "Company Info" - }, - { - "fieldname": "date_of_incorporation", - "fieldtype": "Date", - "label": "Date of Incorporation" - }, - { - "fieldname": "address_html", - "fieldtype": "HTML" - }, - { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, - { - "depends_on": "eval:doc.date_of_incorporation", - "fieldname": "date_of_commencement", - "fieldtype": "Date", - "label": "Date of Commencement" - }, - { - "fieldname": "phone_no", - "fieldtype": "Data", - "label": "Phone No", - "oldfieldname": "phone_no", - "oldfieldtype": "Data", - "options": "Phone" - }, - { - "fieldname": "fax", - "fieldtype": "Data", - "label": "Fax", - "oldfieldname": "fax", - "oldfieldtype": "Data", - "options": "Phone" - }, - { - "fieldname": "email", - "fieldtype": "Data", - "label": "Email", - "oldfieldname": "email", - "oldfieldtype": "Data", - "options": "Email" - }, - { - "fieldname": "website", - "fieldtype": "Data", - "label": "Website", - "oldfieldname": "website", - "oldfieldtype": "Data" - }, - { - "fieldname": "registration_info", - "fieldtype": "Section Break", - "oldfieldtype": "Section Break", - "width": "50%" - }, - { - "description": "Company registration numbers for your reference. Tax numbers etc.", - "fieldname": "registration_details", - "fieldtype": "Code", - "label": "Registration Details", - "oldfieldname": "registration_details", - "oldfieldtype": "Code" - }, - { - "fieldname": "delete_company_transactions", - "fieldtype": "Button", - "label": "Delete Company Transactions" - }, - { - "fieldname": "lft", - "fieldtype": "Int", - "hidden": 1, - "label": "Lft", - "print_hide": 1, - "read_only": 1, - "search_index": 1 - }, - { - "fieldname": "rgt", - "fieldtype": "Int", - "hidden": 1, - "label": "Rgt", - "print_hide": 1, - "read_only": 1, - "search_index": 1 - }, - { - "fieldname": "old_parent", - "fieldtype": "Data", - "hidden": 1, - "label": "old_parent", - "print_hide": 1, - "read_only": 1 - } - ], - "icon": "fa fa-building", - "idx": 1, - "image_field": "company_logo", - "modified": "2019-06-14 14:36:11.363309", - "modified_by": "Administrator", - "module": "Setup", - "name": "Company", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "role": "Accounts User" - }, - { - "read": 1, - "role": "Employee" - }, - { - "read": 1, - "role": "Sales User" - }, - { - "read": 1, - "role": "Purchase User" - }, - { - "read": 1, - "role": "Stock User" - }, - { - "read": 1, - "role": "Projects User" - } - ], - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1 - } \ No newline at end of file + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:company_name", + "creation": "2013-04-10 08:35:39", + "description": "Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Organization.", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "details", + "company_name", + "abbr", + "change_abbr", + "is_group", + "cb0", + "domain", + "parent_company", + "charts_section", + "default_currency", + "default_letter_head", + "default_holiday_list", + "default_finance_book", + "standard_working_hours", + "default_selling_terms", + "default_buying_terms", + "default_warehouse_for_sales_return", + "column_break_10", + "country", + "create_chart_of_accounts_based_on", + "chart_of_accounts", + "existing_company", + "tax_id", + "date_of_establishment", + "sales_settings", + "monthly_sales_target", + "sales_monthly_history", + "column_break_goals", + "transactions_annual_history", + "total_monthly_sales", + "default_settings", + "default_bank_account", + "default_cash_account", + "default_receivable_account", + "round_off_account", + "round_off_cost_center", + "write_off_account", + "discount_allowed_account", + "discount_received_account", + "exchange_gain_loss_account", + "unrealized_exchange_gain_loss_account", + "column_break0", + "allow_account_creation_against_child_company", + "default_payable_account", + "default_employee_advance_account", + "default_expense_account", + "default_income_account", + "default_deferred_revenue_account", + "default_deferred_expense_account", + "default_payroll_payable_account", + "default_expense_claim_payable_account", + "section_break_22", + "cost_center", + "column_break_26", + "credit_limit", + "payment_terms", + "auto_accounting_for_stock_settings", + "enable_perpetual_inventory", + "default_inventory_account", + "stock_adjustment_account", + "column_break_32", + "stock_received_but_not_billed", + "expenses_included_in_valuation", + "fixed_asset_depreciation_settings", + "accumulated_depreciation_account", + "depreciation_expense_account", + "series_for_depreciation_entry", + "expenses_included_in_asset_valuation", + "column_break_40", + "disposal_account", + "depreciation_cost_center", + "capital_work_in_progress_account", + "asset_received_but_not_billed", + "budget_detail", + "exception_budget_approver_role", + "company_info", + "company_logo", + "date_of_incorporation", + "address_html", + "date_of_commencement", + "phone_no", + "fax", + "email", + "website", + "column_break1", + "company_description", + "registration_info", + "registration_details", + "delete_company_transactions", + "lft", + "rgt", + "old_parent" + ], + "fields": [ + { + "fieldname": "details", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "company_name", + "fieldtype": "Data", + "label": "Company", + "oldfieldname": "company_name", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "abbr", + "fieldtype": "Data", + "label": "Abbr", + "oldfieldname": "abbr", + "oldfieldtype": "Data", + "reqd": 1 + }, + { + "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", + "fieldname": "change_abbr", + "fieldtype": "Button", + "label": "Change Abbreviation" + }, + { + "bold": 1, + "default": "0", + "fieldname": "is_group", + "fieldtype": "Check", + "label": "Is Group" + }, + { + "fieldname": "default_finance_book", + "fieldtype": "Link", + "label": "Default Finance Book", + "options": "Finance Book" + }, + { + "fieldname": "cb0", + "fieldtype": "Column Break" + }, + { + "fieldname": "domain", + "fieldtype": "Link", + "label": "Domain", + "options": "Domain" + }, + { + "fieldname": "parent_company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Parent Company", + "options": "Company" + }, + { + "fieldname": "company_logo", + "fieldtype": "Attach Image", + "hidden": 1, + "label": "Company Logo" + }, + { + "fieldname": "company_description", + "fieldtype": "Text Editor", + "label": "Company Description" + }, + { + "collapsible": 1, + "fieldname": "sales_settings", + "fieldtype": "Section Break", + "label": "Sales Settings" + }, + { + "fieldname": "sales_monthly_history", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Sales Monthly History", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "transactions_annual_history", + "fieldtype": "Code", + "hidden": 1, + "label": "Transactions Annual History", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "monthly_sales_target", + "fieldtype": "Currency", + "label": "Monthly Sales Target", + "options": "default_currency" + }, + { + "fieldname": "column_break_goals", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_monthly_sales", + "fieldtype": "Currency", + "label": "Total Monthly Sales", + "no_copy": 1, + "options": "default_currency", + "read_only": 1 + }, + { + "fieldname": "charts_section", + "fieldtype": "Section Break", + "label": "Default Values" + }, + { + "fieldname": "default_currency", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Currency", + "options": "Currency", + "reqd": 1 + }, + { + "fieldname": "default_letter_head", + "fieldtype": "Link", + "label": "Default Letter Head", + "options": "Letter Head" + }, + { + "fieldname": "default_holiday_list", + "fieldtype": "Link", + "label": "Default Holiday List", + "options": "Holiday List" + }, + { + "fieldname": "standard_working_hours", + "fieldtype": "Float", + "label": "Standard Working Hours" + }, + { + "fieldname": "default_warehouse_for_sales_return", + "fieldtype": "Link", + "label": "Default warehouse for Sales Return", + "options": "Warehouse" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "country", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Country", + "options": "Country", + "reqd": 1 + }, + { + "fieldname": "create_chart_of_accounts_based_on", + "fieldtype": "Select", + "label": "Create Chart Of Accounts Based On", + "options": "\nStandard Template\nExisting Company" + }, + { + "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"", + "fieldname": "chart_of_accounts", + "fieldtype": "Select", + "label": "Chart Of Accounts Template", + "no_copy": 1 + }, + { + "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"", + "fieldname": "existing_company", + "fieldtype": "Link", + "label": "Existing Company ", + "no_copy": 1, + "options": "Company" + }, + { + "fieldname": "tax_id", + "fieldtype": "Data", + "label": "Tax ID" + }, + { + "fieldname": "date_of_establishment", + "fieldtype": "Date", + "label": "Date of Establishment" + }, + { + "fieldname": "default_settings", + "fieldtype": "Section Break", + "label": "Accounts Settings", + "oldfieldtype": "Section Break" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_bank_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Bank Account", + "no_copy": 1, + "oldfieldname": "default_bank_account", + "oldfieldtype": "Link", + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_cash_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Cash Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_receivable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Receivable Account", + "no_copy": 1, + "oldfieldname": "receivables_group", + "oldfieldtype": "Link", + "options": "Account" + }, + { + "fieldname": "round_off_account", + "fieldtype": "Link", + "label": "Round Off Account", + "options": "Account" + }, + { + "fieldname": "round_off_cost_center", + "fieldtype": "Link", + "label": "Round Off Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "write_off_account", + "fieldtype": "Link", + "label": "Write Off Account", + "options": "Account" + }, + { + "fieldname": "discount_allowed_account", + "fieldtype": "Link", + "label": "Discount Allowed Account", + "options": "Account" + }, + { + "fieldname": "discount_received_account", + "fieldtype": "Link", + "label": "Discount Received Account", + "options": "Account" + }, + { + "fieldname": "exchange_gain_loss_account", + "fieldtype": "Link", + "label": "Exchange Gain / Loss Account", + "options": "Account" + }, + { + "fieldname": "unrealized_exchange_gain_loss_account", + "fieldtype": "Link", + "label": "Unrealized Exchange Gain/Loss Account", + "options": "Account" + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "default": "0", + "depends_on": "eval:doc.parent_company", + "fieldname": "allow_account_creation_against_child_company", + "fieldtype": "Check", + "label": "Allow Account Creation Against Child Company" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_payable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Payable Account", + "no_copy": 1, + "oldfieldname": "payables_group", + "oldfieldtype": "Link", + "options": "Account" + }, + { + "fieldname": "default_employee_advance_account", + "fieldtype": "Link", + "label": "Default Employee Advance Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_expense_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Cost of Goods Sold Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_income_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Income Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_deferred_revenue_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Deferred Revenue Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_deferred_expense_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Deferred Expense Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_payroll_payable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Payroll Payable Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_expense_claim_payable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Expense Claim Payable Account", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "section_break_22", + "fieldtype": "Section Break" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "cost_center", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Cost Center", + "no_copy": 1, + "options": "Cost Center" + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "credit_limit", + "fieldtype": "Currency", + "label": "Credit Limit", + "oldfieldname": "credit_limit", + "oldfieldtype": "Currency", + "options": "default_currency" + }, + { + "fieldname": "payment_terms", + "fieldtype": "Link", + "label": "Default Payment Terms Template", + "options": "Payment Terms Template" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "auto_accounting_for_stock_settings", + "fieldtype": "Section Break", + "label": "Stock Settings" + }, + { + "default": "1", + "fieldname": "enable_perpetual_inventory", + "fieldtype": "Check", + "label": "Enable Perpetual Inventory" + }, + { + "fieldname": "default_inventory_account", + "fieldtype": "Link", + "label": "Default Inventory Account", + "options": "Account" + }, + { + "fieldname": "stock_adjustment_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Stock Adjustment Account", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "column_break_32", + "fieldtype": "Column Break" + }, + { + "fieldname": "stock_received_but_not_billed", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Stock Received But Not Billed", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "expenses_included_in_valuation", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Expenses Included In Valuation", + "no_copy": 1, + "options": "Account" + }, + { + "collapsible": 1, + "fieldname": "fixed_asset_depreciation_settings", + "fieldtype": "Section Break", + "label": "Fixed Asset Depreciation Settings" + }, + { + "fieldname": "accumulated_depreciation_account", + "fieldtype": "Link", + "label": "Accumulated Depreciation Account", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "depreciation_expense_account", + "fieldtype": "Link", + "label": "Depreciation Expense Account", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "series_for_depreciation_entry", + "fieldtype": "Data", + "label": "Series for Asset Depreciation Entry (Journal Entry)" + }, + { + "fieldname": "expenses_included_in_asset_valuation", + "fieldtype": "Link", + "label": "Expenses Included In Asset Valuation", + "options": "Account" + }, + { + "fieldname": "column_break_40", + "fieldtype": "Column Break" + }, + { + "fieldname": "disposal_account", + "fieldtype": "Link", + "label": "Gain/Loss Account on Asset Disposal", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "depreciation_cost_center", + "fieldtype": "Link", + "label": "Asset Depreciation Cost Center", + "no_copy": 1, + "options": "Cost Center" + }, + { + "fieldname": "capital_work_in_progress_account", + "fieldtype": "Link", + "label": "Capital Work In Progress Account", + "options": "Account" + }, + { + "fieldname": "asset_received_but_not_billed", + "fieldtype": "Link", + "label": "Asset Received But Not Billed", + "options": "Account" + }, + { + "collapsible": 1, + "fieldname": "budget_detail", + "fieldtype": "Section Break", + "label": "Budget Detail" + }, + { + "fieldname": "exception_budget_approver_role", + "fieldtype": "Link", + "label": "Exception Budget Approver Role", + "options": "Role" + }, + { + "collapsible": 1, + "description": "For reference only.", + "fieldname": "company_info", + "fieldtype": "Section Break", + "label": "Company Info" + }, + { + "fieldname": "date_of_incorporation", + "fieldtype": "Date", + "label": "Date of Incorporation" + }, + { + "fieldname": "address_html", + "fieldtype": "HTML" + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "depends_on": "eval:doc.date_of_incorporation", + "fieldname": "date_of_commencement", + "fieldtype": "Date", + "label": "Date of Commencement" + }, + { + "fieldname": "phone_no", + "fieldtype": "Data", + "label": "Phone No", + "oldfieldname": "phone_no", + "oldfieldtype": "Data", + "options": "Phone" + }, + { + "fieldname": "fax", + "fieldtype": "Data", + "label": "Fax", + "oldfieldname": "fax", + "oldfieldtype": "Data", + "options": "Phone" + }, + { + "fieldname": "email", + "fieldtype": "Data", + "label": "Email", + "oldfieldname": "email", + "oldfieldtype": "Data", + "options": "Email" + }, + { + "fieldname": "website", + "fieldtype": "Data", + "label": "Website", + "oldfieldname": "website", + "oldfieldtype": "Data" + }, + { + "fieldname": "registration_info", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "width": "50%" + }, + { + "description": "Company registration numbers for your reference. Tax numbers etc.", + "fieldname": "registration_details", + "fieldtype": "Code", + "label": "Registration Details", + "oldfieldname": "registration_details", + "oldfieldtype": "Code" + }, + { + "fieldname": "delete_company_transactions", + "fieldtype": "Button", + "label": "Delete Company Transactions" + }, + { + "fieldname": "lft", + "fieldtype": "Int", + "hidden": 1, + "label": "Lft", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "rgt", + "fieldtype": "Int", + "hidden": 1, + "label": "Rgt", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "old_parent", + "fieldtype": "Data", + "hidden": 1, + "label": "old_parent", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "default_selling_terms", + "fieldtype": "Link", + "label": "Default Selling Terms", + "options": "Terms and Conditions" + }, + { + "fieldname": "default_buying_terms", + "fieldtype": "Link", + "label": "Default Buying Terms", + "options": "Terms and Conditions" + } + ], + "icon": "fa fa-building", + "idx": 1, + "image_field": "company_logo", + "modified": "2019-07-04 22:20:45.104307", + "modified_by": "Administrator", + "module": "Setup", + "name": "Company", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts User" + }, + { + "read": 1, + "role": "Employee" + }, + { + "read": 1, + "role": "Sales User" + }, + { + "read": 1, + "role": "Purchase User" + }, + { + "read": 1, + "role": "Stock User" + }, + { + "read": 1, + "role": "Projects User" + } + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json index c1d459f265..aba6a791a4 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json @@ -1,288 +1,142 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, "autoname": "field:title", - "beta": 0, "creation": "2013-01-10 16:34:24", - "custom": 0, "description": "Standard Terms and Conditions that can be added to Sales and Purchases.\n\nExamples:\n\n1. Validity of the offer.\n1. Payment Terms (In Advance, On Credit, part advance etc).\n1. What is extra (or payable by the Customer).\n1. Safety / usage warning.\n1. Warranty if any.\n1. Returns Policy.\n1. Terms of shipping, if applicable.\n1. Ways of addressing disputes, indemnity, liability, etc.\n1. Address and Contact of your Company.", - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, + "engine": "InnoDB", + "field_order": [ + "title", + "disabled", + "applicable_modules_section", + "selling", + "buying", + "hr", + "section_break_7", + "terms", + "terms_and_conditions_help" + ], "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": 0, - "in_standard_filter": 0, "label": "Title", - "length": 0, "no_copy": 1, "oldfieldname": "title", "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, + "default": "0", "fieldname": "disabled", "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": "Disabled", - "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 + "label": "Disabled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "allow_in_quick_entry": 1, "fieldname": "terms", "fieldtype": "Text Editor", - "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": "Terms and Conditions", - "length": 0, - "no_copy": 0, "oldfieldname": "terms", - "oldfieldtype": "Text Editor", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Text Editor" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "terms_and_conditions_help", "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Terms and Conditions Help", - "length": 0, - "no_copy": 0, - "options": "

Standard Terms and Conditions Example

\n\n
Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n
\n\n

How to get fieldnames

\n\n

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Langauge. To learn more about Jinja, read this documentation.

", - "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 + "options": "

Standard Terms and Conditions Example

\n\n
Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n
\n\n

How to get fieldnames

\n\n

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Langauge. To learn more about Jinja, read this documentation.

" + }, + { + "fieldname": "applicable_modules_section", + "fieldtype": "Section Break", + "label": "Applicable Modules" + }, + { + "default": "1", + "fieldname": "selling", + "fieldtype": "Check", + "label": "Selling" + }, + { + "default": "1", + "fieldname": "buying", + "fieldtype": "Check", + "label": "Buying" + }, + { + "default": "1", + "fieldname": "hr", + "fieldtype": "Check", + "label": "HR" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "icon-legal", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-29 06:36:33.131473", + "modified": "2019-07-04 13:31:30.393425", "modified_by": "Administrator", "module": "Setup", "name": "Terms and Conditions", "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": "Sales Master Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "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": 0, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales User" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, - "report": 0, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Purchase User" }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "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": 1, - "submit": 0, "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": 0, - "role": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Stock User" } ], "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 + "sort_field": "modified", + "sort_order": "ASC" } \ No newline at end of file diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py index a2152a6eb5..372cc6d3e3 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py @@ -3,9 +3,11 @@ from __future__ import unicode_literals import frappe +from frappe import _, throw import json from frappe.model.document import Document from frappe.utils.jinja import validate_template +from frappe.utils import cint from six import string_types @@ -13,6 +15,8 @@ class TermsandConditions(Document): def validate(self): if self.terms: validate_template(self.terms) + if not cint(self.buying) and not cint(self.selling) and not cint(self.hr) and not cint(self.disabled): + throw(_("At least one of the Applicable Modules should be selected")) @frappe.whitelist() def get_terms_and_conditions(template_name, doc): diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index a5bd93fc2c..4ca43a89b8 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -33,7 +33,7 @@ def boot_session(bootinfo): FROM `tabCompany` LIMIT 1""") and 'Yes' or 'No' - bootinfo.docs += frappe.db.sql("""select name, default_currency, cost_center, default_terms, + bootinfo.docs += frappe.db.sql("""select name, default_currency, cost_center, default_selling_terms, default_buying_terms, default_letter_head, default_bank_account, enable_perpetual_inventory, country from `tabCompany`""", as_dict=1, update={"doctype":":Company"}) From 830f2b642bcaa7fd82f60b598d1a26a096523a4b Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Fri, 5 Jul 2019 09:48:38 +0530 Subject: [PATCH 17/83] chore(manufacturing): added dashboard for doctypes --- .../blanket_order/blanket_order_dashboard.py | 12 +++++++++ .../doctype/bom/bom_dashboard.py | 27 +++++++++++++++++++ .../doctype/operation/operation_dashboard.py | 13 +++++++++ .../doctype/routing/routing_dashboard.py | 12 +++++++++ .../workstation/workstation_dashboard.py | 13 +++++++++ erpnext/stock/doctype/item/item_dashboard.py | 2 +- 6 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py create mode 100644 erpnext/manufacturing/doctype/bom/bom_dashboard.py create mode 100644 erpnext/manufacturing/doctype/operation/operation_dashboard.py create mode 100644 erpnext/manufacturing/doctype/routing/routing_dashboard.py create mode 100644 erpnext/manufacturing/doctype/workstation/workstation_dashboard.py diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py new file mode 100644 index 0000000000..0005439cb1 --- /dev/null +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'blanket_order', + 'transactions': [ + { + 'items': ['Purchase Order', 'Sales Order'] + } + ] + } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom/bom_dashboard.py b/erpnext/manufacturing/doctype/bom/bom_dashboard.py new file mode 100644 index 0000000000..af710b0464 --- /dev/null +++ b/erpnext/manufacturing/doctype/bom/bom_dashboard.py @@ -0,0 +1,27 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'bom_no', + 'non_standard_fieldnames': { + 'Item': 'default_bom', + 'Purchase Order': 'bom', + 'Purchase Receipt': 'bom', + 'Purchase Invoice': 'bom' + }, + 'transactions': [ + { + 'label': _('Stock'), + 'items': ['Item', 'Stock Entry', 'Quality Inspection'] + }, + { + 'label': _('Manufacture'), + 'items': ['BOM', 'Work Order', 'Job Card', 'Production Plan'] + }, + { + 'label': _('Purchase'), + 'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] + } + ] + } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/operation/operation_dashboard.py b/erpnext/manufacturing/doctype/operation/operation_dashboard.py new file mode 100644 index 0000000000..9e53a20ad5 --- /dev/null +++ b/erpnext/manufacturing/doctype/operation/operation_dashboard.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'operation', + 'transactions': [ + { + 'label': _('Manufacture'), + 'items': ['BOM', 'Work Order', 'Job Card', 'Timesheet'] + } + ] + } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/routing/routing_dashboard.py b/erpnext/manufacturing/doctype/routing/routing_dashboard.py new file mode 100644 index 0000000000..ab309cc9d5 --- /dev/null +++ b/erpnext/manufacturing/doctype/routing/routing_dashboard.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'routing', + 'transactions': [ + { + 'items': ['BOM'] + } + ] + } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py new file mode 100644 index 0000000000..c5683134f2 --- /dev/null +++ b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'workstation', + 'transactions': [ + { + 'label': _('Manufacture'), + 'items': ['BOM', 'Routing', 'Work Order', 'Job Card', 'Operation', 'Timesheet'] + } + ] + } \ No newline at end of file diff --git a/erpnext/stock/doctype/item/item_dashboard.py b/erpnext/stock/doctype/item/item_dashboard.py index b3733d357b..4bf3c38e22 100644 --- a/erpnext/stock/doctype/item/item_dashboard.py +++ b/erpnext/stock/doctype/item/item_dashboard.py @@ -41,7 +41,7 @@ def get_data(): }, { 'label': _('Manufacture'), - 'items': ['Work Order', 'Item Manufacturer'] + 'items': ['Production Plan', 'Work Order', 'Item Manufacturer'] } ] } \ No newline at end of file From ad33c195100abfd35fd1d5ed16998c8ac1a3563b Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Fri, 5 Jul 2019 11:28:59 +0530 Subject: [PATCH 18/83] fix: converting spaces to tabs on a few lines --- .../doctype/blanket_order/blanket_order_dashboard.py | 2 +- erpnext/manufacturing/doctype/bom/bom_dashboard.py | 10 +++++----- .../doctype/job_card/job_card_dashboard.py | 2 +- .../doctype/operation/operation_dashboard.py | 2 +- .../doctype/workstation/workstation_dashboard.py | 2 +- erpnext/stock/doctype/item/item_dashboard.py | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py index 0005439cb1..ed319a0cef 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Purchase Order', 'Sales Order'] } ] - } \ No newline at end of file + } diff --git a/erpnext/manufacturing/doctype/bom/bom_dashboard.py b/erpnext/manufacturing/doctype/bom/bom_dashboard.py index af710b0464..803ece7c78 100644 --- a/erpnext/manufacturing/doctype/bom/bom_dashboard.py +++ b/erpnext/manufacturing/doctype/bom/bom_dashboard.py @@ -6,9 +6,9 @@ def get_data(): 'fieldname': 'bom_no', 'non_standard_fieldnames': { 'Item': 'default_bom', - 'Purchase Order': 'bom', - 'Purchase Receipt': 'bom', - 'Purchase Invoice': 'bom' + 'Purchase Order': 'bom', + 'Purchase Receipt': 'bom', + 'Purchase Invoice': 'bom' }, 'transactions': [ { @@ -19,9 +19,9 @@ def get_data(): 'label': _('Manufacture'), 'items': ['BOM', 'Work Order', 'Job Card', 'Production Plan'] }, - { + { 'label': _('Purchase'), 'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] } ] - } \ No newline at end of file + } diff --git a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py index d48bccf9d4..c2aa2bd968 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py +++ b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['Material Request', 'Stock Entry'] } ] - } \ No newline at end of file + } diff --git a/erpnext/manufacturing/doctype/operation/operation_dashboard.py b/erpnext/manufacturing/doctype/operation/operation_dashboard.py index 9e53a20ad5..8deb9ec6e0 100644 --- a/erpnext/manufacturing/doctype/operation/operation_dashboard.py +++ b/erpnext/manufacturing/doctype/operation/operation_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['BOM', 'Work Order', 'Job Card', 'Timesheet'] } ] - } \ No newline at end of file + } diff --git a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py index c5683134f2..9e0d1d1739 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py +++ b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['BOM', 'Routing', 'Work Order', 'Job Card', 'Operation', 'Timesheet'] } ] - } \ No newline at end of file + } diff --git a/erpnext/stock/doctype/item/item_dashboard.py b/erpnext/stock/doctype/item/item_dashboard.py index 4bf3c38e22..dd4676a037 100644 --- a/erpnext/stock/doctype/item/item_dashboard.py +++ b/erpnext/stock/doctype/item/item_dashboard.py @@ -44,4 +44,4 @@ def get_data(): 'items': ['Production Plan', 'Work Order', 'Item Manufacturer'] } ] - } \ No newline at end of file + } From cdb75b56c5a9c858b2a4b8b87aaa632f1e1a3ec6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 5 Jul 2019 12:20:02 +0530 Subject: [PATCH 19/83] fix: removed item manaufacturer child table from item master --- erpnext/stock/doctype/item/item.json | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 3936bf7524..164ffa443b 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -76,7 +76,6 @@ "is_customer_provided_item", "customer", "supplier_details", - "manufacturers", "delivered_by_supplier", "column_break2", "supplier_items", @@ -1022,12 +1021,6 @@ "fieldtype": "Check", "label": "Synced With Hub", "read_only": 1 - }, - { - "fieldname": "manufacturers", - "fieldtype": "Table", - "label": "Manufacturers", - "options": "Item Manufacturer" } ], "has_web_view": 1, @@ -1035,7 +1028,7 @@ "idx": 2, "image_field": "image", "max_attachments": 1, - "modified": "2019-06-02 04:45:59.911507", + "modified": "2019-07-05 12:18:13.977931", "modified_by": "Administrator", "module": "Stock", "name": "Item", @@ -1097,4 +1090,4 @@ "sort_order": "DESC", "title_field": "item_name", "track_changes": 1 - } \ No newline at end of file + } From 82e2bac89112b74d577db1c917dece714439b651 Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Fri, 5 Jul 2019 12:41:19 +0530 Subject: [PATCH 20/83] fix(coding style): adding a few spaces --- erpnext/manufacturing/doctype/blanket_order/blanket_order.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js index d1bef3fcd0..0bbf689d4a 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js @@ -50,12 +50,12 @@ frappe.ui.form.on('Blanket Order', { }, set_tc_name_filter: function(frm) { - if (frm.doc.blanket_order_type === 'Selling'){ + if (frm.doc.blanket_order_type === 'Selling') { frm.set_query("tc_name", function() { return { filters: { selling: 1 } }; }); } - if (frm.doc.blanket_order_type === 'Purchasing'){ + if (frm.doc.blanket_order_type === 'Purchasing') { frm.set_query("tc_name", function() { return { filters: { buying: 1 } }; }); From 7a7c66e95e281181520ed0f5ea8c1ca5d10a1aa2 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 5 Jul 2019 13:06:53 +0530 Subject: [PATCH 21/83] refactor: added throw if supplier is not default for any item --- .../material_request/material_request.py | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index ef5f24e59d..2b079e7995 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -370,19 +370,20 @@ def make_purchase_order_based_on_supplier(source_name, target_doc=None): def get_material_requests_based_on_supplier(supplier): supplier_items = [d.parent for d in frappe.db.get_all("Item Default", {"default_supplier": supplier}, 'parent')] - if supplier_items: - material_requests = frappe.db.sql_list("""select distinct mr.name - from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item - where mr.name = mr_item.parent - and mr_item.item_code in (%s) - and mr.material_request_type = 'Purchase' - and mr.per_ordered < 99.99 - and mr.docstatus = 1 - and mr.status != 'Stopped' - order by mr_item.item_code ASC""" % ', '.join(['%s']*len(supplier_items)), - tuple(supplier_items)) - else: - material_requests = [] + if not supplier_items: + frappe.throw(_("{0} is not the default supplier for any items.".format(supplier))) + + material_requests = frappe.db.sql_list("""select distinct mr.name + from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item + where mr.name = mr_item.parent + and mr_item.item_code in (%s) + and mr.material_request_type = 'Purchase' + and mr.per_ordered < 99.99 + and mr.docstatus = 1 + and mr.status != 'Stopped' + order by mr_item.item_code ASC""" % ', '.join(['%s']*len(supplier_items)), + tuple(supplier_items)) + return material_requests, supplier_items @frappe.whitelist() From 2f3f7507c4d131d204b473938f9f33a4338983fd Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 28 Apr 2019 23:30:00 +0530 Subject: [PATCH 22/83] fix: browser hangs while making payment entry for large number of outstaning invoices --- .../doctype/payment_entry/payment_entry.js | 87 ++++++++++++------- .../doctype/payment_entry/payment_entry.json | 12 ++- .../doctype/payment_entry/payment_entry.py | 30 ++++--- erpnext/accounts/utils.py | 10 ++- 4 files changed, 86 insertions(+), 53 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 2c382c58f6..986457fac8 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -302,7 +302,7 @@ frappe.ui.form.on('Payment Entry', { }, () => frm.set_value("party_balance", r.message.party_balance), () => frm.set_value("party_name", r.message.party_name), - () => frm.events.get_outstanding_documents(frm), + () => frm.clear_table("references"), () => frm.events.hide_unhide_fields(frm), () => frm.events.set_dynamic_labels(frm), () => { @@ -323,9 +323,7 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_account_currency_and_balance(frm, frm.doc.paid_from, "paid_from_account_currency", "paid_from_account_balance", function(frm) { - if (frm.doc.payment_type == "Receive") { - frm.events.get_outstanding_documents(frm); - } else if (frm.doc.payment_type == "Pay") { + if (frm.doc.payment_type == "Pay") { frm.events.paid_amount(frm); } } @@ -337,9 +335,7 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_account_currency_and_balance(frm, frm.doc.paid_to, "paid_to_account_currency", "paid_to_account_balance", function(frm) { - if(frm.doc.payment_type == "Pay") { - frm.events.get_outstanding_documents(frm); - } else if (frm.doc.payment_type == "Receive") { + if (frm.doc.payment_type == "Receive") { if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) { if(frm.doc.source_exchange_rate) { frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate); @@ -533,26 +529,49 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_unallocated_amount(frm); }, - get_outstanding_documents: function(frm) { + get_outstanding_invoice: function(frm) { + const fields = [ + {fieldtype:"Date", label: __("Posting From Date"), fieldname:"from_date"}, + {fieldtype:"Column Break"}, + {fieldtype:"Date", label: __("Posting To Date"), fieldname:"to_date"}, + {fieldtype:"Section Break"}, + ]; + + frappe.prompt(fields, function(data){ + frappe.flags.allocate_payment_amount = true; + frm.events.get_outstanding_documents(frm, data); + }, __("Select Date"), __("Get Outstanding Invoices")); + }, + + get_outstanding_documents: function(frm, date_args) { frm.clear_table("references"); - if(!frm.doc.party) return; + if(!frm.doc.party) { + return; + } frm.events.check_mandatory_to_fetch(frm); var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; + var args = { + "posting_date": frm.doc.posting_date, + "company": frm.doc.company, + "party_type": frm.doc.party_type, + "payment_type": frm.doc.payment_type, + "party": frm.doc.party, + "party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to, + "cost_center": frm.doc.cost_center + } + + if(date_args) { + args["from_date"] = date_args["from_date"]; + args["to_date"] = date_args["to_date"]; + } + return frappe.call({ method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_outstanding_reference_documents', args: { - args: { - "posting_date": frm.doc.posting_date, - "company": frm.doc.company, - "party_type": frm.doc.party_type, - "payment_type": frm.doc.payment_type, - "party": frm.doc.party, - "party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to, - "cost_center": frm.doc.cost_center - } + args:args }, callback: function(r, rt) { if(r.message) { @@ -608,24 +627,26 @@ frappe.ui.form.on('Payment Entry', { frm.events.allocate_party_amount_against_ref_docs(frm, (frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount)); + + frappe.flags.allocate_payment_amount = false; } }); }, - allocate_payment_amount: function(frm) { - if(frm.doc.payment_type == 'Internal Transfer'){ - return - } + // allocate_payment_amount: function(frm) { + // if(frm.doc.payment_type == 'Internal Transfer'){ + // return + // } - if(frm.doc.references.length == 0){ - frm.events.get_outstanding_documents(frm); - } - if(frm.doc.payment_type == 'Internal Transfer') { - frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount); - } else { - frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount); - } - }, + // if(frm.doc.references.length == 0){ + // frm.events.get_outstanding_documents(frm); + // } + // if(frm.doc.payment_type == 'Internal Transfer') { + // frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount); + // } else { + // frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount); + // } + // }, allocate_party_amount_against_ref_docs: function(frm, paid_amount) { var total_positive_outstanding_including_order = 0; @@ -677,7 +698,7 @@ frappe.ui.form.on('Payment Entry', { $.each(frm.doc.references || [], function(i, row) { row.allocated_amount = 0 //If allocate payment amount checkbox is unchecked, set zero to allocate amount - if(frm.doc.allocate_payment_amount){ + if(frappe.flags.allocate_payment_amount){ if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) { if(row.outstanding_amount >= allocated_positive_outstanding) { row.allocated_amount = allocated_positive_outstanding; @@ -958,7 +979,7 @@ frappe.ui.form.on('Payment Entry', { }, () => { if(frm.doc.payment_type != "Internal") { - frm.events.get_outstanding_documents(frm); + frm.clear_table("references"); } } ]); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index fcaa570331..4950bd12d3 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -325,19 +325,17 @@ "reqd": 1 }, { - "collapsible": 1, - "collapsible_depends_on": "references", + "collapsible": 0, + "collapsible_depends_on": "", "depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)", "fieldname": "section_break_14", "fieldtype": "Section Break", "label": "Reference" }, { - "default": "1", - "depends_on": "eval:in_list(['Pay', 'Receive'], doc.payment_type)", - "fieldname": "allocate_payment_amount", - "fieldtype": "Check", - "label": "Allocate Payment Amount" + "fieldname": "get_outstanding_invoice", + "fieldtype": "Button", + "label": "Get Outstanding Invoice" }, { "fieldname": "references", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index ea76126d8c..bb0d44e9d4 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -574,8 +574,8 @@ def get_outstanding_reference_documents(args): # Get negative outstanding sales /purchase invoices negative_outstanding_invoices = [] if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"): - negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), - args.get("party"), args.get("party_account"), party_account_currency, company_currency) + negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"), + args.get("party_account"), args.get("company"), party_account_currency, company_currency) # Get positive outstanding sales /purchase invoices/ Fees condition = "" @@ -585,10 +585,16 @@ def get_outstanding_reference_documents(args): # Add cost center condition if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account(): - condition += " and cost_center='%s'" % args.get("cost_center") + condition += " and cost_center='%s'" % args.get("cost_center") + + if args.get("from_date") and args.get("to_date"): + condition += " and posting_date between '{0}' and '{1}'".format(args.get("from_date"), args.get("to_date")) + + if args.get("company"): + condition += " and company = '{0}'".format(frappe.db.escape(args.get("company"))) outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"), - args.get("party_account"), condition=condition) + args.get("party_account"), condition=condition, limit=100) for d in outstanding_invoices: d["exchange_rate"] = 1 @@ -606,12 +612,13 @@ def get_outstanding_reference_documents(args): orders_to_be_billed = [] if (args.get("party_type") != "Student"): orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), - args.get("party"), party_account_currency, company_currency) + args.get("party"), args.get("company"), party_account_currency, company_currency) return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed -def get_orders_to_be_billed(posting_date, party_type, party, party_account_currency, company_currency, cost_center=None): +def get_orders_to_be_billed(posting_date, party_type, party, + company, party_account_currency, company_currency, cost_center=None): if party_type == "Customer": voucher_type = 'Sales Order' elif party_type == "Supplier": @@ -641,6 +648,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre where {party_type} = %s and docstatus = 1 + and company = %s and ifnull(status, "") != "Closed" and {ref_field} > advance_paid and abs(100 - per_billed) > 0.01 @@ -652,7 +660,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre "voucher_type": voucher_type, "party_type": scrub(party_type), "condition": condition - }), party, as_dict=True) + }), (party, company), as_dict=True) order_list = [] for d in orders: @@ -663,7 +671,8 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre return order_list -def get_negative_outstanding_invoices(party_type, party, party_account, party_account_currency, company_currency, cost_center=None): +def get_negative_outstanding_invoices(party_type, party, party_account, + company, party_account_currency, company_currency, cost_center=None): voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice" supplier_condition = "" if voucher_type == "Purchase Invoice": @@ -684,7 +693,8 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac from `tab{voucher_type}` where - {party_type} = %s and {party_account} = %s and docstatus = 1 and outstanding_amount < 0 + {party_type} = %s and {party_account} = %s and docstatus = 1 and + company = %s and outstanding_amount < 0 {supplier_condition} order by posting_date, name @@ -696,7 +706,7 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac "party_type": scrub(party_type), "party_account": "debit_to" if party_type == "Customer" else "credit_to", "cost_center": cost_center - }), (party, party_account), as_dict=True) + }), (party, party_account, company), as_dict=True) @frappe.whitelist() diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 7a1f6c57c2..d1861785af 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -632,6 +632,10 @@ def get_outstanding_invoices(party_type, party, account, condition=None): outstanding_invoices = [] precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2 + limit_cond = '' + if limit: + limit_cond = " limit {}".format(limit) + if erpnext.get_party_account_type(party_type) == 'Receivable': dr_or_cr = "debit_in_account_currency - credit_in_account_currency" payment_dr_or_cr = "credit_in_account_currency - debit_in_account_currency" @@ -673,11 +677,11 @@ def get_outstanding_invoices(party_type, party, account, condition=None): and account = %(account)s and {payment_dr_or_cr} > 0 and against_voucher is not null and against_voucher != '' - group by against_voucher_type, against_voucher - """.format(payment_dr_or_cr=payment_dr_or_cr), { + group by against_voucher_type, against_voucher {limit_cond} + """.format(payment_dr_or_cr=payment_dr_or_cr, limit_cond= limit_cond), { "party_type": party_type, "party": party, - "account": account, + "account": account }, as_dict=True) pe_map = frappe._dict() From 0f065d5de163a0eeb778a0e0c3d3d1af1808a1bc Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 2 May 2019 00:06:04 +0530 Subject: [PATCH 23/83] Added due date in the gl entry --- .../accounts/doctype/gl_entry/gl_entry.json | 35 ++++++++- .../doctype/journal_entry/journal_entry.py | 1 + .../doctype/payment_entry/payment_entry.js | 72 ++++++++++++------- .../doctype/payment_entry/payment_entry.json | 6 +- .../doctype/payment_entry/payment_entry.py | 32 ++++++--- .../purchase_invoice/purchase_invoice.py | 1 + .../doctype/sales_invoice/sales_invoice.py | 1 + erpnext/accounts/utils.py | 23 +++--- erpnext/patches.txt | 1 + .../patches/v12_0/update_due_date_in_gle.py | 17 +++++ 10 files changed, 139 insertions(+), 50 deletions(-) create mode 100644 erpnext/patches/v12_0/update_due_date_in_gle.py diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json index 333d39aae6..a232a953f3 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.json +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json @@ -848,6 +848,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, + "fetch_if_empty": 0, + "fieldname": "due_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": "Due 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": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 } ], "has_web_view": 0, @@ -861,7 +894,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-01-07 07:05:00.366399", + "modified": "2019-05-01 07:05:00.366399", "modified_by": "Administrator", "module": "Accounts", "name": "GL Entry", diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 3132c937dd..8fbddb9b0c 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -498,6 +498,7 @@ class JournalEntry(AccountsController): self.get_gl_dict({ "account": d.account, "party_type": d.party_type, + "due_date": self.due_date, "party": d.party, "against": d.against_account, "debit": flt(d.debit, d.precision("debit")), diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 986457fac8..f17b2cbeda 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -530,20 +530,57 @@ frappe.ui.form.on('Payment Entry', { }, get_outstanding_invoice: function(frm) { + const today = frappe.datetime.get_today(); const fields = [ - {fieldtype:"Date", label: __("Posting From Date"), fieldname:"from_date"}, + {fieldtype:"Section Break", label: __("Posting Date")}, + {fieldtype:"Date", label: __("From Date"), + fieldname:"from_posting_date", default:frappe.datetime.add_days(today, -30)}, {fieldtype:"Column Break"}, - {fieldtype:"Date", label: __("Posting To Date"), fieldname:"to_date"}, + {fieldtype:"Date", label: __("To Date"), fieldname:"to_posting_date", default:today}, + {fieldtype:"Section Break", label: __("Due Date")}, + {fieldtype:"Date", label: __("From Date"), fieldname:"from_due_date"}, + {fieldtype:"Column Break"}, + {fieldtype:"Date", label: __("To Date"), fieldname:"to_due_date"}, + {fieldtype:"Section Break", label: __("Outstanding Amount")}, + {fieldtype:"Float", label: __("Greater Than Amount"), + fieldname:"outstanding_amt_greater_than", default: 0}, + {fieldtype:"Column Break"}, + {fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"}, {fieldtype:"Section Break"}, + {fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1}, ]; - frappe.prompt(fields, function(data){ + frappe.prompt(fields, function(filters){ frappe.flags.allocate_payment_amount = true; - frm.events.get_outstanding_documents(frm, data); - }, __("Select Date"), __("Get Outstanding Invoices")); + frm.events.validate_filters_data(frm, filters); + frm.events.get_outstanding_documents(frm, filters); + }, __("Filters"), __("Get Outstanding Invoices")); }, - get_outstanding_documents: function(frm, date_args) { + validate_filters_data: function(frm, filters) { + const fields = { + 'Posting Date': ['from_posting_date', 'to_posting_date'], + 'Due Date': ['from_posting_date', 'to_posting_date'], + 'Advance Amount': ['from_posting_date', 'to_posting_date'], + }; + + for (let key in fields) { + let from_field = fields[key][0]; + let to_field = fields[key][1]; + + if (filters[from_field] && !filters[to_field]) { + frappe.throw(__("Error: {0} is mandatory field", + [to_field.replace(/_/g, " ")] + )); + } else if (filters[from_field] && filters[from_field] > filters[to_field]) { + frappe.throw(__("{0}: {1} must be less than {2}", + [key, from_field.replace(/_/g, " "), to_field.replace(/_/g, " ")] + )); + } + } + }, + + get_outstanding_documents: function(frm, filters) { frm.clear_table("references"); if(!frm.doc.party) { @@ -563,11 +600,12 @@ frappe.ui.form.on('Payment Entry', { "cost_center": frm.doc.cost_center } - if(date_args) { - args["from_date"] = date_args["from_date"]; - args["to_date"] = date_args["to_date"]; + for (let key in filters) { + args[key] = filters[key]; } + frappe.flags.allocate_payment_amount = filters['allocate_payment_amount']; + return frappe.call({ method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_outstanding_reference_documents', args: { @@ -628,26 +666,10 @@ frappe.ui.form.on('Payment Entry', { frm.events.allocate_party_amount_against_ref_docs(frm, (frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount)); - frappe.flags.allocate_payment_amount = false; } }); }, - // allocate_payment_amount: function(frm) { - // if(frm.doc.payment_type == 'Internal Transfer'){ - // return - // } - - // if(frm.doc.references.length == 0){ - // frm.events.get_outstanding_documents(frm); - // } - // if(frm.doc.payment_type == 'Internal Transfer') { - // frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount); - // } else { - // frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount); - // } - // }, - allocate_party_amount_against_ref_docs: function(frm, paid_amount) { var total_positive_outstanding_including_order = 0; var total_negative_outstanding = 0; diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 4950bd12d3..a85eccd30a 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -40,7 +40,7 @@ "target_exchange_rate", "base_received_amount", "section_break_14", - "allocate_payment_amount", + "get_outstanding_invoice", "references", "section_break_34", "total_allocated_amount", @@ -325,8 +325,6 @@ "reqd": 1 }, { - "collapsible": 0, - "collapsible_depends_on": "", "depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)", "fieldname": "section_break_14", "fieldtype": "Section Break", @@ -568,7 +566,7 @@ } ], "is_submittable": 1, - "modified": "2019-05-25 22:02:40.575822", + "modified": "2019-05-27 15:53:21.108857", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index bb0d44e9d4..699f04675f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -587,14 +587,21 @@ def get_outstanding_reference_documents(args): if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account(): condition += " and cost_center='%s'" % args.get("cost_center") - if args.get("from_date") and args.get("to_date"): - condition += " and posting_date between '{0}' and '{1}'".format(args.get("from_date"), args.get("to_date")) + date_fields_dict = { + 'posting_date': ['from_posting_date', 'to_posting_date'], + 'due_date': ['from_due_date', 'to_due_date'] + } + + for fieldname, date_fields in date_fields_dict.items(): + if args.get(date_fields[0]) and args.get(date_fields[1]): + condition += " and {0} between '{1}' and '{2}'".format(fieldname, + args.get(date_fields[0]), args.get(date_fields[1])) if args.get("company"): - condition += " and company = '{0}'".format(frappe.db.escape(args.get("company"))) + condition += " and company = {0}".format(frappe.db.escape(args.get("company"))) outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"), - args.get("party_account"), condition=condition, limit=100) + args.get("party_account"), filters=args, condition=condition, limit=100) for d in outstanding_invoices: d["exchange_rate"] = 1 @@ -612,13 +619,19 @@ def get_outstanding_reference_documents(args): orders_to_be_billed = [] if (args.get("party_type") != "Student"): orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), - args.get("party"), args.get("company"), party_account_currency, company_currency) + args.get("party"), args.get("company"), party_account_currency, company_currency, filters=args) - return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed + data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed + + if not data: + frappe.msgprint(_("No outstanding invoices found for the {0} {1}.") + .format(args.get("party_type").lower(), args.get("party"))) + + return data def get_orders_to_be_billed(posting_date, party_type, party, - company, party_account_currency, company_currency, cost_center=None): + company, party_account_currency, company_currency, cost_center=None, filters=None): if party_type == "Customer": voucher_type = 'Sales Order' elif party_type == "Supplier": @@ -664,6 +677,10 @@ def get_orders_to_be_billed(posting_date, party_type, party, order_list = [] for d in orders: + if not (d.outstanding_amount >= filters.get("outstanding_amt_greater_than") + and d.outstanding_amount <= filters.get("outstanding_amt_less_than")): + continue + d["voucher_type"] = voucher_type # This assumes that the exchange rate required is the one in the SO d["exchange_rate"] = get_exchange_rate(party_account_currency, company_currency, posting_date) @@ -934,7 +951,6 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= pe.paid_to_account_currency = party_account_currency if payment_type=="Pay" else bank.account_currency pe.paid_amount = paid_amount pe.received_amount = received_amount - pe.allocate_payment_amount = 1 pe.letter_head = doc.get("letter_head") if pe.party_type in ["Customer", "Supplier"]: diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index a6f6acea66..1a49be3399 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -416,6 +416,7 @@ class PurchaseInvoice(BuyingController): "account": self.credit_to, "party_type": "Supplier", "party": self.supplier, + "due_date": self.due_date, "against": self.against_expense_account, "credit": grand_total_in_company_currency, "credit_in_account_currency": grand_total_in_company_currency \ diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index b725c73c8b..6d44811f19 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -734,6 +734,7 @@ class SalesInvoice(SellingController): "account": self.debit_to, "party_type": "Customer", "party": self.customer, + "due_date": self.due_date, "against": self.against_income_account, "debit": grand_total_in_company_currency, "debit_in_account_currency": grand_total_in_company_currency \ diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index d1861785af..542c7e4e52 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -628,14 +628,10 @@ def get_held_invoices(party_type, party): return held_invoices -def get_outstanding_invoices(party_type, party, account, condition=None): +def get_outstanding_invoices(party_type, party, account, condition=None, filters=None): outstanding_invoices = [] precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2 - limit_cond = '' - if limit: - limit_cond = " limit {}".format(limit) - if erpnext.get_party_account_type(party_type) == 'Receivable': dr_or_cr = "debit_in_account_currency - credit_in_account_currency" payment_dr_or_cr = "credit_in_account_currency - debit_in_account_currency" @@ -648,7 +644,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None): invoice_list = frappe.db.sql(""" select - voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount + voucher_no, voucher_type, posting_date, due_date, + ifnull(sum({dr_or_cr}), 0) as invoice_amount from `tabGL Entry` where @@ -677,8 +674,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None): and account = %(account)s and {payment_dr_or_cr} > 0 and against_voucher is not null and against_voucher != '' - group by against_voucher_type, against_voucher {limit_cond} - """.format(payment_dr_or_cr=payment_dr_or_cr, limit_cond= limit_cond), { + group by against_voucher_type, against_voucher + """.format(payment_dr_or_cr=payment_dr_or_cr), { "party_type": party_type, "party": party, "account": account @@ -692,10 +689,12 @@ def get_outstanding_invoices(party_type, party, account, condition=None): payment_amount = pe_map.get((d.voucher_type, d.voucher_no), 0) outstanding_amount = flt(d.invoice_amount - payment_amount, precision) if outstanding_amount > 0.5 / (10**precision): - if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices: - due_date = frappe.db.get_value( - d.voucher_type, d.voucher_no, "posting_date" if party_type == "Employee" else "due_date") + if (filters.get("outstanding_amt_greater_than") and + not (outstanding_amount >= filters.get("outstanding_amt_greater_than") and + outstanding_amount <= filters.get("outstanding_amt_less_than"))): + continue + if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices: outstanding_invoices.append( frappe._dict({ 'voucher_no': d.voucher_no, @@ -704,7 +703,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None): 'invoice_amount': flt(d.invoice_amount), 'payment_amount': payment_amount, 'outstanding_amount': outstanding_amount, - 'due_date': due_date + 'due_date': d.due_date }) ) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 72db8ad063..b35a6da586 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -615,3 +615,4 @@ erpnext.patches.v11_1.set_missing_opportunity_from erpnext.patches.v12_0.set_quotation_status erpnext.patches.v12_0.set_priority_for_support erpnext.patches.v12_0.delete_priority_property_setter +erpnext.patches.v12_0.update_due_date_in_gle diff --git a/erpnext/patches/v12_0/update_due_date_in_gle.py b/erpnext/patches/v12_0/update_due_date_in_gle.py new file mode 100644 index 0000000000..9e2be6fcee --- /dev/null +++ b/erpnext/patches/v12_0/update_due_date_in_gle.py @@ -0,0 +1,17 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("accounts", "doctype", "gl_entry") + + for doctype in ["Sales Invoice", "Purchase Invoice", "Journal Entry"]: + frappe.reload_doc("accounts", "doctype", frappe.scrub(doctype)) + + frappe.db.sql(""" UPDATE `tabGL Entry`, `tab{doctype}` + SET + `tabGL Entry`.due_date = `tab{doctype}`.due_date + WHERE + `tabGL Entry`.voucher_no = `tab{doctype}`.name and `tabGL Entry`.party is not null + and `tabGL Entry`.voucher_type in ('Sales Invoice', 'Purchase Invoice', 'Journal Entry') + and account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable') )""" + .format(doctype=doctype)) \ No newline at end of file From 909d7734626d65e8e179b28c8a8545abbbfab401 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 4 Jul 2019 19:32:46 +0530 Subject: [PATCH 24/83] fix: on credit note / debit note deferred reversed instead of income --- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 6 +++++- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index a6f6acea66..1b03896dab 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -484,9 +484,13 @@ class PurchaseInvoice(BuyingController): "credit": flt(item.rm_supp_cost) }, warehouse_account[self.supplier_warehouse]["account_currency"], item=item)) elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()): + + expense_account = (item.expense_account + if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account) + gl_entries.append( self.get_gl_dict({ - "account": item.expense_account if not item.enable_deferred_expense else item.deferred_expense_account, + "account": expense_account, "against": self.supplier, "debit": flt(item.base_net_amount, item.precision("base_net_amount")), "debit_in_account_currency": (flt(item.base_net_amount, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index b725c73c8b..71bdc2aafe 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -783,10 +783,13 @@ class SalesInvoice(SellingController): asset.db_set("disposal_date", self.posting_date) asset.set_status("Sold" if self.docstatus==1 else None) else: - account_currency = get_account_currency(item.income_account) + income_account = (item.income_account + if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account) + + account_currency = get_account_currency(income_account) gl_entries.append( self.get_gl_dict({ - "account": item.income_account if not item.enable_deferred_revenue else item.deferred_revenue_account, + "account": income_account, "against": self.customer, "credit": flt(item.base_net_amount, item.precision("base_net_amount")), "credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount")) From ee01ba587902ee15b7e08ee9cb0a98a2b55cdd3a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 5 Jul 2019 15:21:45 +0530 Subject: [PATCH 25/83] fix: linting --- erpnext/patches/v12_0/update_due_date_in_gle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/update_due_date_in_gle.py b/erpnext/patches/v12_0/update_due_date_in_gle.py index 9e2be6fcee..4c47a82dcc 100644 --- a/erpnext/patches/v12_0/update_due_date_in_gle.py +++ b/erpnext/patches/v12_0/update_due_date_in_gle.py @@ -13,5 +13,5 @@ def execute(): WHERE `tabGL Entry`.voucher_no = `tab{doctype}`.name and `tabGL Entry`.party is not null and `tabGL Entry`.voucher_type in ('Sales Invoice', 'Purchase Invoice', 'Journal Entry') - and account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable') )""" - .format(doctype=doctype)) \ No newline at end of file + and account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable') )""" #nosec + .format(doctype=doctype)) From 6277966105a8765178ddc69e4487ccbfd4aa3a72 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 5 Jul 2019 15:58:13 +0530 Subject: [PATCH 26/83] chore: added check for auto-reorder in stock settings (#18174) * chore: added check for auto-reorder in stock settings * style: removed explicit return --- erpnext/stock/doctype/item/item.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 80d4e172a5..6484b93485 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -122,6 +122,7 @@ class Item(WebsiteGenerator): self.validate_item_defaults() self.validate_customer_provided_part() self.update_defaults_from_item_group() + self.validate_auto_reorder_enabled_in_stock_settings() self.cant_change() if not self.get("__islocal"): @@ -859,6 +860,12 @@ class Item(WebsiteGenerator): filters={"production_item": self.name, "docstatus": 1}): return True + def validate_auto_reorder_enabled_in_stock_settings(self): + if self.reorder_levels: + enabled = frappe.db.get_single_value('Stock Settings', 'auto_indent') + if not enabled: + frappe.msgprint(msg=_("You have to enable auto re-order in Stock Settings to maintain re-order levels."), title=_("Enable Auto Re-Order"), indicator="orange") + def get_timeline_data(doctype, name): '''returns timeline data based on stock ledger entry''' out = {} From 5d4d70b75f1404e28bfca4c435fcfb6d992d19fe Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 5 Jul 2019 16:59:27 +0530 Subject: [PATCH 27/83] feat: provision to make debit / credit note against the stock returned entry --- .../purchase_invoice/purchase_invoice.py | 6 +++-- erpnext/controllers/status_updater.py | 2 +- .../doctype/delivery_note/delivery_note.js | 26 +++++++++++++++++++ .../doctype/delivery_note/delivery_note.py | 5 +++- .../purchase_receipt/purchase_receipt.js | 23 ++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 10 +++++++ 6 files changed, 68 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 4d87edf375..18a38d8c20 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -337,7 +337,8 @@ class PurchaseInvoice(BuyingController): if not self.is_return: self.update_against_document_in_jv() self.update_billing_status_for_zero_amount_refdoc("Purchase Order") - self.update_billing_status_in_pr() + + self.update_billing_status_in_pr() # Updating stock ledger should always be called after updating prevdoc status, # because updating ordered qty in bin depends upon updated ordered qty in PO @@ -769,7 +770,8 @@ class PurchaseInvoice(BuyingController): if not self.is_return: self.update_billing_status_for_zero_amount_refdoc("Purchase Order") - self.update_billing_status_in_pr() + + self.update_billing_status_in_pr() # Updating stock ledger should always be called after updating prevdoc status, # because updating ordered qty in bin depends upon updated ordered qty in PO diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 42e0a43e6e..62be2e4e2a 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -290,7 +290,7 @@ class StatusUpdater(Document): frappe.db.sql("""update `tab%(target_parent_dt)s` set %(target_parent_field)s = round( ifnull((select - ifnull(sum(if(%(target_ref_field)s > %(target_field)s, abs(%(target_field)s), abs(%(target_ref_field)s))), 0) + ifnull(sum(if(abs(%(target_ref_field)s) > abs(%(target_field)s), abs(%(target_field)s), abs(%(target_ref_field)s))), 0) / sum(abs(%(target_ref_field)s)) * 100 from `tab%(target_dt)s` where parent="%(name)s" having sum(abs(%(target_ref_field)s)) > 0), 0), 6) %(update_modified)s diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 78bc06a47b..8c4a5cd11c 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -77,8 +77,34 @@ frappe.ui.form.on("Delivery Note", { }, + print_without_amount: function(frm) { erpnext.stock.delivery_note.set_print_hide(frm.doc); + }, + + refresh: function(frm) { + if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) { + frm.add_custom_button(__('Credit Note'), function() { + frappe.confirm(__("Are you sure you want to make credit note?"), + function() { + frm.trigger("make_credit_note"); + } + ); + }, __('Create')); + + frm.page.set_inner_btn_group_as_primary(__('Create')); + } + }, + + make_credit_note: function(frm) { + frm.call({ + method: "make_return_invoice", + doc: frm.doc, + freeze: true, + callback: function() { + frm.reload_doc(); + } + }); } }); diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 1e522b834e..ec7df2da6d 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -333,7 +333,10 @@ class DeliveryNote(SellingController): return_invoice.is_return = True return_invoice.save() return_invoice.submit() - frappe.msgprint(_("Credit Note {0} has been created automatically").format(return_invoice.name)) + + credit_note_link = frappe.utils.get_link_to_form('Sales Invoice', return_invoice.name) + + frappe.msgprint(_("Credit Note {0} has been created automatically").format(credit_note_link)) except: frappe.throw(_("Could not create Credit Note automatically, please uncheck 'Issue Credit Note' and submit again")) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index e82aa2c63e..a2d3e75f23 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -38,6 +38,29 @@ frappe.ui.form.on("Purchase Receipt", { if(frm.doc.company) { frm.trigger("toggle_display_account_head"); } + + if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) { + frm.add_custom_button(__('Debit Note'), function() { + frappe.confirm(__("Are you sure you want to make debit note?"), + function() { + frm.trigger("make_debit_note"); + } + ); + }, __('Create')); + + frm.page.set_inner_btn_group_as_primary(__('Create')); + } + }, + + make_debit_note: function(frm) { + frm.call({ + method: "make_return_invoice", + doc: frm.doc, + freeze: true, + callback: function() { + frm.reload_doc(); + } + }); }, company: function(frm) { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index cdca44d60b..11e60b0438 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -396,6 +396,16 @@ class PurchaseReceipt(BuyingController): self.load_from_db() + def make_return_invoice(self): + return_invoice = make_purchase_invoice(self.name) + return_invoice.is_return = True + return_invoice.save() + return_invoice.submit() + + debit_note_link = frappe.utils.get_link_to_form('Purchase Invoice', return_invoice.name) + + frappe.msgprint(_("Debit Note {0} has been created automatically").format(debit_note_link)) + def update_billed_amount_based_on_po(po_detail, update_modified=True): # Billed against Sales Order directly billed_against_po = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item` From db3c5625aedc97b6b4b43438960fe0944371d14f Mon Sep 17 00:00:00 2001 From: Bhavishya Sharma Date: Sat, 6 Jul 2019 13:54:30 +0530 Subject: [PATCH 28/83] feat: Manufacturer is Missing Contacts --- .../doctype/manufacturer/manufacturer.js | 10 +- .../doctype/manufacturer/manufacturer.json | 358 +++++------------- .../doctype/manufacturer/manufacturer.py | 5 +- 3 files changed, 118 insertions(+), 255 deletions(-) diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.js b/erpnext/stock/doctype/manufacturer/manufacturer.js index 67df1bf4c0..dcbe2a05f0 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.js +++ b/erpnext/stock/doctype/manufacturer/manufacturer.js @@ -3,6 +3,14 @@ frappe.ui.form.on('Manufacturer', { refresh: function(frm) { - + frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Manufacturer' } + if (frm.doc.__islocal) { + hide_field(['address_html','contact_html']); + frappe.contacts.clear_address_and_contact(frm); + } + else { + unhide_field(['address_html','contact_html']); + frappe.contacts.render_address_and_contact(frm); + } } }); diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.json b/erpnext/stock/doctype/manufacturer/manufacturer.json index 5025917836..3a64fbeceb 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.json +++ b/erpnext/stock/doctype/manufacturer/manufacturer.json @@ -1,268 +1,120 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:short_name", - "beta": 0, - "creation": "2016-01-17 11:04:52.761731", - "custom": 0, - "description": "Manufacturers used in Items", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:short_name", + "creation": "2016-01-17 11:04:52.761731", + "description": "Manufacturers used in Items", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "short_name", + "full_name", + "website", + "country", + "logo", + "address_contacts", + "address_html", + "column_break_8", + "contact_html", + "section_break_10", + "notes" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Limited to 12 characters", - "fieldname": "short_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": "Short 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 - }, + "description": "Limited to 12 characters", + "fieldname": "short_name", + "fieldtype": "Data", + "label": "Short Name", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "full_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": "Full 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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "full_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Full Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "website", - "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": "Website", - "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, - "unique": 0 - }, + "fieldname": "website", + "fieldtype": "Data", + "label": "Website" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "country", - "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": "Country", - "length": 0, - "no_copy": 0, - "options": "Country", - "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 - }, + "fieldname": "country", + "fieldtype": "Link", + "label": "Country", + "options": "Country" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "logo", - "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": "Logo", - "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, - "unique": 0 - }, + "fieldname": "logo", + "fieldtype": "Attach Image", + "label": "Logo" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "notes", - "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": "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, - "unique": 0 + "fieldname": "notes", + "fieldtype": "Small Text", + "label": "Notes" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "address_contacts", + "fieldtype": "Section Break", + "label": "Address and Contacts", + "options": "fa fa-map-marker" + }, + { + "fieldname": "address_html", + "fieldtype": "HTML", + "label": "Address HTML", + "read_only": 1 + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "fieldname": "contact_html", + "fieldtype": "HTML", + "label": "Contact HTML", + "read_only": 1 + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-certificate", - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-08-03 06:27:57.182666", - "modified_by": "Administrator", - "module": "Stock", - "name": "Manufacturer", - "name_case": "", - "owner": "Administrator", + ], + "icon": "fa fa-certificate", + "modified": "2019-07-06 13:06:47.237014", + "modified_by": "Administrator", + "module": "Stock", + "name": "Manufacturer", + "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": "Stock Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "short_name, full_name", - "show_name_in_global_search": 1, - "sort_field": "", - "sort_order": "DESC", - "title_field": "short_name", - "track_changes": 0, - "track_seen": 0 + ], + "search_fields": "short_name, full_name", + "show_name_in_global_search": 1, + "sort_order": "DESC", + "title_field": "short_name" } \ No newline at end of file diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.py b/erpnext/stock/doctype/manufacturer/manufacturer.py index 7b85b05aa1..b624f73b77 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.py +++ b/erpnext/stock/doctype/manufacturer/manufacturer.py @@ -4,7 +4,10 @@ from __future__ import unicode_literals import frappe +from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address from frappe.model.document import Document class Manufacturer(Document): - pass + def onload(self): + """Load address and contacts in `__onload`""" + load_address_and_contact(self) From 607611ebdc68186d051f2d8c4b72006e061b3bf7 Mon Sep 17 00:00:00 2001 From: Bhavishya Sharma Date: Sat, 6 Jul 2019 15:02:57 +0530 Subject: [PATCH 29/83] codacy fix --- erpnext/stock/doctype/manufacturer/manufacturer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.js b/erpnext/stock/doctype/manufacturer/manufacturer.js index dcbe2a05f0..bb7e314e14 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.js +++ b/erpnext/stock/doctype/manufacturer/manufacturer.js @@ -3,7 +3,7 @@ frappe.ui.form.on('Manufacturer', { refresh: function(frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Manufacturer' } + frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Manufacturer' }; if (frm.doc.__islocal) { hide_field(['address_html','contact_html']); frappe.contacts.clear_address_and_contact(frm); From 7ce199807cafc3c25c634adeb13b8924a081c578 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Sun, 7 Jul 2019 14:25:08 +0530 Subject: [PATCH 30/83] fix: GSTR-1 B2CS Json file generation and cess amount fixes --- erpnext/regional/report/gstr_1/gstr_1.py | 55 ++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index eff578001f..e8c170e721 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -74,7 +74,6 @@ class Gstr1Report(object): for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): invoice_details = self.invoices.get(inv) - for rate, items in items_based_on_rate.items(): place_of_supply = invoice_details.get("place_of_supply") ecommerce_gstin = invoice_details.get("ecommerce_gstin") @@ -85,7 +84,7 @@ class Gstr1Report(object): "rate": "", "taxable_value": 0, "cess_amount": 0, - "type": 0 + "type": "" }) row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin)) @@ -94,6 +93,7 @@ class Gstr1Report(object): row["rate"] = rate row["taxable_value"] += sum([abs(net_amount) for item_code, net_amount in self.invoice_items.get(inv).items() if item_code in items]) + row["cess_amount"] += flt(self.invoice_cess.get(inv), 2) row["type"] = "E" if ecommerce_gstin else "OE" for key, value in iteritems(b2cs_output): @@ -123,6 +123,10 @@ class Gstr1Report(object): row += [tax_rate or 0, taxable_value] + for column in self.other_columns: + if column.get('fieldname') == 'cess_amount': + row.append(flt(self.invoice_cess.get(invoice), 2)) + return row, taxable_value def get_invoice_data(self): @@ -327,7 +331,7 @@ class Gstr1Report(object): "fieldtype": "Data" }, { - "fieldname": "invoice_type", + "fieldname": "gst_category", "label": "Invoice Type", "fieldtype": "Data" }, @@ -564,12 +568,18 @@ def get_json(): out = get_b2b_json(res, gstin) gst_json["b2b"] = out + elif filters["type_of_business"] == "B2C Large": for item in report_data[:-1]: res.setdefault(item["place_of_supply"], []).append(item) out = get_b2cl_json(res, gstin) gst_json["b2cl"] = out + + elif filters["type_of_business"] == "B2C Small": + out = get_b2cs_json(report_data[:-1], gstin) + gst_json["b2cs"] = out + elif filters["type_of_business"] == "EXPORT": for item in report_data[:-1]: res.setdefault(item["export_type"], []).append(item) @@ -605,6 +615,45 @@ def get_b2b_json(res, gstin): return out +def get_b2cs_json(data, gstin): + + company_state_number = gstin[0:2] + + out = [] + for d in data: + + pos = d.get('place_of_supply').split('-')[0] + tax_details = {} + + rate = d.get('rate', 0) + tax = flt((d["taxable_value"]*rate)/100.0, 2) + + if company_state_number == pos: + tax_details.update({"camt": flt(tax/2.0, 2), "samt": flt(tax/2.0, 2)}) + else: + tax_details.update({"iamt": tax}) + + inv = { + "sply_ty": "INTRA" if company_state_number == pos else "INTER", + "pos": pos, + "typ": d.get('type'), + "txval": flt(d.get('taxable_value'), 2), + "rt": rate, + "iamt": flt(tax_details.get('iamt'), 2), + "camt": flt(tax_details.get('camt'), 2), + "samt": flt(tax_details.get('samt'), 2), + "csamt": flt(d.get('cess_amount'), 2) + } + + if d.get('type') == "E" and d.get('ecommerce_gstin'): + inv.update({ + "etin": d.get('ecommerce_gstin') + }) + + out.append(inv) + + return out + def get_b2cl_json(res, gstin): out = [] for pos in res: From 6bb5ade667120dd5ec01cb2ea5d2aed9aa86eb6b Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Sun, 7 Jul 2019 21:24:45 +0530 Subject: [PATCH 31/83] fix: Add accounting dimensions to various reports and fixes --- .../accounting_dimension.js | 29 +++++++++++++------ .../accounting_dimension.json | 5 ++-- .../accounting_dimension.py | 6 ++-- .../accounts_payable/accounts_payable.js | 11 +++++++ .../accounts_payable_summary.js | 11 +++++++ .../accounts_receivable.js | 11 +++++++ .../accounts_receivable.py | 9 ++++++ .../accounts_receivable_summary.js | 11 +++++++ .../budget_variance_report.js | 4 +-- .../report/general_ledger/general_ledger.js | 4 +-- .../profitability_analysis.js | 9 ++++-- .../profitability_analysis.py | 19 ++++++++---- .../report/sales_register/sales_register.js | 11 +++++++ .../report/sales_register/sales_register.py | 11 +++++++ .../report/trial_balance/trial_balance.js | 4 +-- erpnext/public/js/financial_statements.js | 4 +-- .../public/js/utils/dimension_tree_filter.js | 2 +- 17 files changed, 127 insertions(+), 34 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js index fcbd10f606..dd20632a65 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js @@ -9,6 +9,26 @@ frappe.ui.form.on('Accounting Dimension', { frappe.set_route("List", frm.doc.document_type); }); } + + let button = frm.doc.disabled ? "Enable" : "Disable"; + + frm.add_custom_button(__(button), function() { + + frm.set_value('disabled', 1 - frm.doc.disabled); + + frappe.call({ + method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension", + args: { + doc: frm.doc + }, + freeze: true, + callback: function(r) { + let message = frm.doc.disabled ? "Dimension Disabled" : "Dimension Enabled"; + frm.save(); + frappe.show_alert({message:__(message), indicator:'green'}); + } + }); + }); }, document_type: function(frm) { @@ -21,13 +41,4 @@ frappe.ui.form.on('Accounting Dimension', { } }); }, - - disabled: function(frm) { - frappe.call({ - method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension", - args: { - doc: frm.doc - } - }); - } }); diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json index 1e2bb925a1..57543a0ef4 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json @@ -38,7 +38,8 @@ "default": "0", "fieldname": "disabled", "fieldtype": "Check", - "label": "Disable" + "label": "Disable", + "read_only": 1 }, { "default": "0", @@ -53,7 +54,7 @@ "label": "Mandatory For Profit and Loss Account" } ], - "modified": "2019-05-27 18:18:17.792726", + "modified": "2019-07-07 18:56:19.517450", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting Dimension", diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 91d03be493..15ace7239e 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -121,11 +121,11 @@ def delete_accounting_dimension(doc): @frappe.whitelist() def disable_dimension(doc): if frappe.flags.in_test: - frappe.enqueue(start_dimension_disabling, doc=doc) + toggle_disabling(doc=doc) else: - start_dimension_disabling(doc=doc) + frappe.enqueue(toggle_disabling, doc=doc) -def start_dimension_disabling(doc): +def toggle_disabling(doc): doc = json.loads(doc) if doc.get('disabled'): diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 70f193e787..f6a561f04f 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -108,3 +108,14 @@ frappe.query_reports["Accounts Payable"] = { }); } } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); 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 06499adeea..ec4f0c983f 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -92,3 +92,14 @@ frappe.query_reports["Accounts Payable Summary"] = { }); } } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 3661afe797..2a45454bac 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -172,3 +172,14 @@ frappe.query_reports["Accounts Receivable"] = { }); } } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 29737484c7..0cda2c15dd 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _, scrub from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions class ReceivablePayableReport(object): def __init__(self, filters=None): @@ -553,6 +554,14 @@ class ReceivablePayableReport(object): conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts))) values += accounts + accounting_dimensions = get_accounting_dimensions() + + if accounting_dimensions: + for dimension in accounting_dimensions: + if self.filters.get(dimension): + conditions.append("{0} = %s".format(dimension)) + values.append(self.filters.get(dimension)) + return " and ".join(conditions), values def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher): 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 f9162adabd..a7c0787fcd 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -116,3 +116,14 @@ frappe.query_reports["Accounts Receivable Summary"] = { }); } } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js index b2072f06f1..f2a33a83ee 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -63,9 +63,7 @@ frappe.query_reports["Budget Variance Report"] = { ] } -let dimension_filters = erpnext.get_dimension_filters(); - -dimension_filters.then((dimensions) => { +erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]); }); diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 32af644021..ea82575b80 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -159,9 +159,7 @@ frappe.query_reports["General Ledger"] = { ] } -let dimension_filters = erpnext.get_dimension_filters(); - -dimension_filters.then((dimensions) => { +erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{ "fieldname": dimension["fieldname"], diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index 80b50b92c3..d6864b54f7 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -16,7 +16,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "fieldname": "based_on", "label": __("Based On"), "fieldtype": "Select", - "options": "Cost Center\nProject", + "options": ["Cost Center", "Project"], "default": "Cost Center", "reqd": 1 }, @@ -104,5 +104,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "parent_field": "parent_account", "initial_depth": 3 } -}); + erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Profitability Analysis"].filters[1].options.push(dimension["document_type"]); + }); + }); +}); diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py index a0d8c5f0e4..6e9b31f2f6 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py @@ -24,8 +24,17 @@ def get_accounts_data(based_on, company): if based_on == 'cost_center': return frappe.db.sql("""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt from `tabCost Center` where company=%s order by name""", company, as_dict=True) - else: + elif based_on == 'project': return frappe.get_all('Project', fields = ["name"], filters = {'company': company}, order_by = 'name') + else: + filters = {} + doctype = frappe.unscrub(based_on) + has_company = frappe.db.has_column(doctype, 'company') + + if has_company: + filters.update({'company': company}) + + return frappe.get_all(doctype, fields = ["name"], filters = filters, order_by = 'name') def get_data(accounts, filters, based_on): if not accounts: @@ -42,7 +51,7 @@ def get_data(accounts, filters, based_on): accumulate_values_into_parents(accounts, accounts_by_name) data = prepare_data(accounts, filters, total_row, parent_children_map, based_on) - data = filter_out_zero_value_rows(data, parent_children_map, + data = filter_out_zero_value_rows(data, parent_children_map, show_zero_values=filters.get("show_zero_values")) return data @@ -112,14 +121,14 @@ def prepare_data(accounts, filters, total_row, parent_children_map, based_on): for key in value_fields: row[key] = flt(d.get(key, 0.0), 3) - + if abs(row[key]) >= 0.005: # ignore zero values has_value = True row["has_value"] = has_value data.append(row) - + data.extend([{},total_row]) return data @@ -174,7 +183,7 @@ def set_gl_entries_by_account(company, from_date, to_date, based_on, gl_entries_ if from_date: additional_conditions.append("and posting_date >= %(from_date)s") - gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit, + gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit, is_opening, (select root_type from `tabAccount` where name = account) as type from `tabGL Entry` where company=%(company)s {additional_conditions} diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js index 0b48882ca9..442aa1262e 100644 --- a/erpnext/accounts/report/sales_register/sales_register.js +++ b/erpnext/accounts/report/sales_register/sales_register.js @@ -67,3 +67,14 @@ frappe.query_reports["Sales Register"] = { } ] } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index de60995ca2..d08056f6f9 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import flt from frappe import msgprint, _ +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions def execute(filters=None): return _execute(filters) @@ -163,6 +164,16 @@ def get_conditions(filters): where parent=`tabSales Invoice`.name and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)""" + accounting_dimensions = get_accounting_dimensions() + + if accounting_dimensions: + for dimension in accounting_dimensions: + if filters.get(dimension): + conditions += """ and exists(select name from `tabSales Invoice Item` + where parent=`tabSales Invoice`.name + and ifnull(`tabSales Invoice Item`.{0}, '') = %({0})s)""".format(dimension) + + return conditions def get_invoices(filters, additional_query_columns): diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 8bc72807b3..73d2ab3898 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -96,9 +96,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { } }); -let dimension_filters = erpnext.get_dimension_filters(); - -dimension_filters.then((dimensions) => { +erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{ "fieldname": dimension["fieldname"], diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index d1113a4ca4..89cb13d981 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -129,9 +129,7 @@ function get_filters(){ } ] - let dimension_filters = erpnext.get_dimension_filters(); - - dimension_filters.then((dimensions) => { + erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { filters.push({ "fieldname": dimension["fieldname"], diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index fef450795b..a9122d8dff 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -7,7 +7,7 @@ erpnext.doctypes_with_dimensions = ["GL Entry", "Sales Invoice", "Purchase Invoi "Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation", "Travel Request", "Fees", "POS Profile"]; -let dimension_filters = erpnext.get_dimension_filters(); +erpnext.dimension_filters = erpnext.get_dimension_filters(); erpnext.doctypes_with_dimensions.forEach((doctype) => { frappe.ui.form.on(doctype, { From 35f15148fb60df17d709925ee81155d98c7422c6 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Mon, 8 Jul 2019 04:55:28 +0000 Subject: [PATCH 32/83] fix: do not update modified (#18193) --- erpnext/support/doctype/issue/issue.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 93f13f1f9f..ad1c263250 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -258,15 +258,15 @@ def set_service_level_agreement_variance(issue=None): if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer variance = round(time_diff_in_hours(doc.response_by, current_time), 2) - frappe.db.set_value("Issue", doc.name, "response_by_variance", variance) + frappe.db.set_value(dt="Issue", dn=doc.name, field="response_by_variance", val=variance, update_modified=False) if variance < 0: - frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed") + frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False) if not doc.resolution_date: # resolution_date set when issue has been closed variance = round(time_diff_in_hours(doc.resolution_by, current_time), 2) - frappe.db.set_value("Issue", doc.name, "resolution_by_variance", variance) + frappe.db.set_value(dt="Issue", dn=doc.name, field="resolution_by_variance", val=variance, update_modified=False) if variance < 0: - frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed") + frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False) def get_list_context(context=None): return { From 334335a2efdcb86a41230a57cb6e41f1e1342c25 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 8 Jul 2019 10:26:29 +0530 Subject: [PATCH 33/83] fix: not able to make credit note for the sales invoice in which item code is not set (#18184) --- erpnext/controllers/sales_and_purchase_return.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 8cf11f785b..2fddcdf24c 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -75,7 +75,7 @@ def validate_returned_items(doc): items_returned = False for d in doc.get("items"): - if flt(d.qty) < 0 or d.get('received_qty') < 0: + if d.item_code and (flt(d.qty) < 0 or d.get('received_qty') < 0): if d.item_code not in valid_items: frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}") .format(d.idx, d.item_code, doc.doctype, doc.return_against)) @@ -107,6 +107,9 @@ def validate_returned_items(doc): items_returned = True + elif d.item_name: + items_returned = True + if not items_returned: frappe.throw(_("Atleast one item should be entered with negative quantity in return document")) From d86f027ce011bd194c092998d531f2192d9c599f Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 8 Jul 2019 10:29:26 +0530 Subject: [PATCH 34/83] fix: default supplier was not set from the patch in item defaults for multi company instance (#18178) --- erpnext/patches.txt | 1 + ...pdate_default_supplier_in_item_defaults.py | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0dd88e94e0..70bad34209 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -615,5 +615,6 @@ erpnext.patches.v11_1.set_missing_opportunity_from erpnext.patches.v12_0.set_quotation_status erpnext.patches.v12_0.set_priority_for_support erpnext.patches.v12_0.delete_priority_property_setter +erpnext.patches.v11_1.update_default_supplier_in_item_defaults erpnext.patches.v12_0.update_due_date_in_gle erpnext.patches.v12_0.add_default_buying_selling_terms_in_company diff --git a/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py b/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py new file mode 100644 index 0000000000..347dec1f74 --- /dev/null +++ b/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py @@ -0,0 +1,25 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + ''' + default supplier was not set in the item defaults for multi company instance, + this patch will set the default supplier + + ''' + if not frappe.db.has_column('Item', 'default_supplier'): + return + + frappe.reload_doc('stock', 'doctype', 'item_default') + frappe.reload_doc('stock', 'doctype', 'item') + + companies = frappe.get_all("Company") + if len(companies) > 1: + frappe.db.sql(""" UPDATE `tabItem Default`, `tabItem` + SET `tabItem Default`.default_supplier = `tabItem`.default_supplier + WHERE + `tabItem Default`.parent = `tabItem`.name and `tabItem Default`.default_supplier is null + and `tabItem`.default_supplier is not null and `tabItem`.default_supplier != '' """) \ No newline at end of file From bef897602d5ab745bcd0051da7843f11fcf6b3fd Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Mon, 8 Jul 2019 10:30:26 +0530 Subject: [PATCH 35/83] fix: Use db_set since it triggers on_update event (#18175) --- .../doctype/bank_reconciliation/bank_reconciliation.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 28807c4118..90cdf834c5 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -103,7 +103,7 @@ class BankReconciliation(Document): for d in self.get('payment_entries'): if d.clearance_date: if not d.payment_document: - frappe.throw(_("Row #{0}: Payment document is required to complete the trasaction")) + frappe.throw(_("Row #{0}: Payment document is required to complete the transaction")) if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date): frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}") @@ -113,10 +113,8 @@ class BankReconciliation(Document): if not d.clearance_date: d.clearance_date = None - frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date) - frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s - where name=%s""".format(d.payment_document), - (d.clearance_date, nowdate(), d.payment_entry)) + payment_entry = frappe.get_doc(d.payment_document, d.payment_entry) + payment_entry.db_set('clearance_date', d.clearance_date) clearance_date_updated = True From 8309fcfbbc6cc5a97568aa988b993ebc8157e4e7 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 8 Jul 2019 10:39:30 +0530 Subject: [PATCH 36/83] BREAKING CHANGE: Remove anti-pattern "Project Task" (#18059) * BREAKING CHANGE: Remove anti-pattern "Project Task" * fix(tests): remove `tasks` from project/test_records.json * fix(tests) * fix(test): test_employee_onboarding.py * fix(tests): test_expense_claim.py * fix(refactor): cleanup project.py validate/update * fix(refactor): cleanup project.py validate/update * fix(test): test_expense_claim * fix(test): test_expense_claim * fix(test): test_expense_claim, try Test Company 4 * Update project.py --- .../test_employee_onboarding.py | 10 +- .../expense_claim/test_expense_claim.py | 52 +- erpnext/patches.txt | 1 + erpnext/patches/v12_0/set_task_status.py | 3 +- erpnext/projects/doctype/project/project.js | 71 - erpnext/projects/doctype/project/project.json | 1715 ++--------------- erpnext/projects/doctype/project/project.py | 254 +-- .../projects/doctype/project/test_project.py | 18 +- .../doctype/project/test_records.json | 8 +- .../projects/doctype/project_task/__init__.py | 0 .../doctype/project_task/project_task.json | 430 ----- .../doctype/project_task/project_task.py | 9 - erpnext/projects/doctype/task/task.py | 6 - .../doctype/sales_order/sales_order.py | 6 - 14 files changed, 187 insertions(+), 2396 deletions(-) delete mode 100644 erpnext/projects/doctype/project_task/__init__.py delete mode 100644 erpnext/projects/doctype/project_task/project_task.json delete mode 100644 erpnext/projects/doctype/project_task/project_task.py diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py index 5e7f276ccc..35c9f728b6 100644 --- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py +++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py @@ -12,7 +12,7 @@ from erpnext.hr.doctype.employee_onboarding.employee_onboarding import Incomplet class TestEmployeeOnboarding(unittest.TestCase): def test_employee_onboarding_incomplete_task(self): if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}): - return frappe.get_doc('Employee Onboarding', {'employee_name': 'Test Researcher'}) + frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'}) _set_up() applicant = get_job_applicant() onboarding = frappe.new_doc('Employee Onboarding') @@ -39,9 +39,10 @@ class TestEmployeeOnboarding(unittest.TestCase): # complete the task project = frappe.get_doc('Project', onboarding.project) - project.load_tasks() - project.tasks[0].status = 'Completed' - project.save() + for task in frappe.get_all('Task', dict(project=project.name)): + task = frappe.get_doc('Task', task.name) + task.status = 'Completed' + task.save() # make employee onboarding.reload() @@ -71,4 +72,3 @@ def _set_up(): project = "Employee Onboarding : Test Researcher - test@researcher.com" frappe.db.sql("delete from tabProject where name=%s", project) frappe.db.sql("delete from tabTask where project=%s", project) - frappe.db.sql("delete from `tabProject Task` where parent=%s", project) diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index 92fdc09443..6618a4f7c5 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -10,33 +10,36 @@ from erpnext.accounts.doctype.account.test_account import create_account test_records = frappe.get_test_records('Expense Claim') test_dependencies = ['Employee'] +company_name = '_Test Company 4' + class TestExpenseClaim(unittest.TestCase): def test_total_expense_claim_for_project(self): frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """) - frappe.db.sql("""delete from `tabProject Task` where parent = "_Test Project 1" """) frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """) - frappe.db.sql("delete from `tabExpense Claim` where project='_Test Project 1'") + frappe.db.sql("update `tabExpense Claim` set project = '', task = ''") frappe.get_doc({ "project_name": "_Test Project 1", - "doctype": "Project", + "doctype": "Project" }).save() - task = frappe.get_doc({ - "doctype": "Task", - "subject": "_Test Project Task 1", - "project": "_Test Project 1" - }).save() + task = frappe.get_doc(dict( + doctype = 'Task', + subject = '_Test Project Task 1', + status = 'Open', + project = '_Test Project 1' + )).insert() - task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"}) - payable_account = get_payable_account("Wind Power LLC") - make_expense_claim(payable_account, 300, 200, "Wind Power LLC","Travel Expenses - WP", "_Test Project 1", task_name) + task_name = task.name + payable_account = get_payable_account(company_name) + + make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", "_Test Project 1", task_name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200) - expense_claim2 = make_expense_claim(payable_account, 600, 500, "Wind Power LLC", "Travel Expenses - WP","_Test Project 1", task_name) + expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4","_Test Project 1", task_name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700) @@ -48,8 +51,8 @@ class TestExpenseClaim(unittest.TestCase): self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200) def test_expense_claim_status(self): - payable_account = get_payable_account("Wind Power LLC") - expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP") + payable_account = get_payable_account(company_name) + expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4") je_dict = make_bank_entry("Expense Claim", expense_claim.name) je = frappe.get_doc(je_dict) @@ -66,9 +69,9 @@ class TestExpenseClaim(unittest.TestCase): self.assertEqual(expense_claim.status, "Unpaid") def test_expense_claim_gl_entry(self): - payable_account = get_payable_account("Wind Power LLC") + payable_account = get_payable_account(company_name) taxes = generate_taxes() - expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP", do_not_submit=True, taxes=taxes) + expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", do_not_submit=True, taxes=taxes) expense_claim.submit() gl_entries = frappe.db.sql("""select account, debit, credit @@ -78,9 +81,9 @@ class TestExpenseClaim(unittest.TestCase): self.assertTrue(gl_entries) expected_values = dict((d[0], d) for d in [ - ['CGST - WP',10.0, 0.0], - [payable_account, 0.0, 210.0], - ["Travel Expenses - WP", 200.0, 0.0] + ['CGST - _TC4',18.0, 0.0], + [payable_account, 0.0, 218.0], + ["Travel Expenses - _TC4", 200.0, 0.0] ]) for gle in gl_entries: @@ -89,14 +92,14 @@ class TestExpenseClaim(unittest.TestCase): self.assertEquals(expected_values[gle.account][2], gle.credit) def test_rejected_expense_claim(self): - payable_account = get_payable_account("Wind Power LLC") + payable_account = get_payable_account(company_name) expense_claim = frappe.get_doc({ "doctype": "Expense Claim", "employee": "_T-Employee-00001", "payable_account": payable_account, "approval_status": "Rejected", "expenses": - [{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "amount": 300, "sanctioned_amount": 200 }] + [{ "expense_type": "Travel", "default_account": "Travel Expenses - _TC4", "amount": 300, "sanctioned_amount": 200 }] }) expense_claim.submit() @@ -111,9 +114,9 @@ def get_payable_account(company): def generate_taxes(): parent_account = frappe.db.get_value('Account', - {'company': "Wind Power LLC", 'is_group':1, 'account_type': 'Tax'}, + {'company': company_name, 'is_group':1, 'account_type': 'Tax'}, 'name') - account = create_account(company="Wind Power LLC", account_name="CGST", account_type="Tax", parent_account=parent_account) + account = create_account(company=company_name, account_name="CGST", account_type="Tax", parent_account=parent_account) return {'taxes':[{ "account_head": account, "rate": 0, @@ -124,15 +127,18 @@ def generate_taxes(): def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None): employee = frappe.db.get_value("Employee", {"status": "Active"}) + currency = frappe.db.get_value('Company', company, 'default_currency') expense_claim = { "doctype": "Expense Claim", "employee": employee, "payable_account": payable_account, "approval_status": "Approved", "company": company, + 'currency': currency, "expenses": [{"expense_type": "Travel", "default_account": account, + 'currency': currency, "amount": amount, "sanctioned_amount": sanctioned_amount}]} if taxes: diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 70bad34209..571c2dc75b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -615,6 +615,7 @@ erpnext.patches.v11_1.set_missing_opportunity_from erpnext.patches.v12_0.set_quotation_status erpnext.patches.v12_0.set_priority_for_support erpnext.patches.v12_0.delete_priority_property_setter +execute:frappe.delete_doc("DocType", "Project Task") erpnext.patches.v11_1.update_default_supplier_in_item_defaults erpnext.patches.v12_0.update_due_date_in_gle erpnext.patches.v12_0.add_default_buying_selling_terms_in_company diff --git a/erpnext/patches/v12_0/set_task_status.py b/erpnext/patches/v12_0/set_task_status.py index 32b8177130..70f65097dc 100644 --- a/erpnext/patches/v12_0/set_task_status.py +++ b/erpnext/patches/v12_0/set_task_status.py @@ -2,10 +2,9 @@ import frappe def execute(): frappe.reload_doctype('Task') - frappe.reload_doctype('Project Task') # add "Completed" if customized - for doctype in ('Task', 'Project Task'): + for doctype in ('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) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 528c7cd0c7..5613f088e1 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -1,23 +1,6 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Project", { - setup: function (frm) { - frm.set_indicator_formatter('title', - function (doc) { - let indicator = 'orange'; - if (doc.status == 'Overdue') { - indicator = 'red'; - } else if (doc.status == 'Cancelled') { - indicator = 'dark grey'; - } else if (doc.status == 'Completed') { - indicator = 'green'; - } - return indicator; - } - ); - }, - - onload: function (frm) { var so = frappe.meta.get_docfield("Project", "sales_order"); so.get_route_options_for_new_doc = function (field) { @@ -99,58 +82,4 @@ frappe.ui.form.on("Project", { }); }, - tasks_refresh: function (frm) { - var grid = frm.get_field('tasks').grid; - grid.wrapper.find('select[data-fieldname="status"]').each(function () { - if ($(this).val() === 'Open') { - $(this).addClass('input-indicator-open'); - } else { - $(this).removeClass('input-indicator-open'); - } - }); - }, - - status: function(frm) { - if (frm.doc.status === 'Cancelled') { - frappe.confirm(__('Set tasks in this project as cancelled?'), () => { - frm.doc.tasks = frm.doc.tasks.map(task => { - task.status = 'Cancelled'; - return task; - }); - frm.refresh_field('tasks'); - }); - } - } -}); - -frappe.ui.form.on("Project Task", { - edit_task: function(frm, doctype, name) { - var doc = frappe.get_doc(doctype, name); - if(doc.task_id) { - frappe.set_route("Form", "Task", doc.task_id); - } else { - frappe.msgprint(__("Save the document first.")); - } - }, - - edit_timesheet: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - frappe.route_options = {"project": frm.doc.project_name, "task": child.task_id}; - frappe.set_route("List", "Timesheet"); - }, - - make_timesheet: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - frappe.model.with_doctype('Timesheet', function() { - var doc = frappe.model.get_new_doc('Timesheet'); - var row = frappe.model.add_child(doc, 'time_logs'); - row.project = frm.doc.project_name; - row.task = child.task_id; - frappe.set_route('Form', doc.doctype, doc.name); - }) - }, - - status: function(frm, doctype, name) { - frm.trigger('tasks_refresh'); - }, }); diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 2fc507b252..b4536c085c 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -1,1974 +1,487 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, "autoname": "field:project_name", - "beta": 0, "creation": "2013-03-07 11:55:07", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "project_name", + "status", + "project_type", + "is_active", + "percent_complete_method", + "percent_complete", + "column_break_5", + "project_template", + "expected_start_date", + "expected_end_date", + "priority", + "department", + "customer_details", + "customer", + "column_break_14", + "sales_order", + "users_section", + "users", + "copied_from", + "section_break0", + "notes", + "section_break_18", + "actual_start_date", + "actual_time", + "column_break_20", + "actual_end_date", + "project_details", + "estimated_costing", + "total_costing_amount", + "total_expense_claim", + "total_purchase_cost", + "company", + "column_break_28", + "total_sales_amount", + "total_billable_amount", + "total_billed_amount", + "total_consumed_material_cost", + "cost_center", + "margin", + "gross_margin", + "column_break_37", + "per_gross_margin", + "monitor_progress", + "collect_progress", + "holiday_list", + "frequency", + "from_time", + "to_time", + "first_email", + "second_email", + "daily_time_to_send", + "day_to_send", + "weekly_time_to_send", + "column_break_45", + "message" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "project_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": "Project Name", - "length": 0, - "no_copy": 0, "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, "default": "Open", "fieldname": "status", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Status", - "length": 0, "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", "options": "Open\nCompleted\nCancelled", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "project_type", "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": 1, "label": "Project Type", - "length": 0, - "no_copy": 0, "oldfieldname": "project_type", "oldfieldtype": "Data", - "options": "Project Type", - "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 + "options": "Project Type" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "is_active", "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": "Is Active", - "length": 0, - "no_copy": 0, "oldfieldname": "is_active", "oldfieldtype": "Select", - "options": "Yes\nNo", - "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 + "options": "Yes\nNo" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Task Completion", "fieldname": "percent_complete_method", "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": "% Complete Method", - "length": 0, - "no_copy": 0, - "options": "Task Completion\nTask Progress\nTask Weight", - "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 + "options": "Task Completion\nTask Progress\nTask Weight" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "fieldname": "percent_complete", "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "% Completed", - "length": 0, "no_copy": 1, - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_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, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "project_template", "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": "From Template", - "length": 0, - "no_copy": 0, - "options": "Project Template", - "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 + "options": "Project Template" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "expected_start_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 Start Date", - "length": 0, - "no_copy": 0, "oldfieldname": "project_start_date", - "oldfieldtype": "Date", - "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 + "oldfieldtype": "Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "fieldname": "expected_end_date", "fieldtype": "Date", - "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": "Expected End Date", - "length": 0, - "no_copy": 0, "oldfieldname": "completion_date", - "oldfieldtype": "Date", - "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 + "oldfieldtype": "Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "priority", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Priority", - "length": 0, - "no_copy": 0, "oldfieldname": "priority", "oldfieldtype": "Select", - "options": "Medium\nLow\nHigh", - "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 + "options": "Medium\nLow\nHigh" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "department", "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": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "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 + "options": "Department" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "customer_details", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Customer Details", - "length": 0, - "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-user", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "fa fa-user" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "customer", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Customer", - "length": 0, - "no_copy": 0, "oldfieldname": "customer", "oldfieldtype": "Link", "options": "Customer", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_14", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sales_order", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Sales Order", - "length": 0, - "no_copy": 0, - "options": "Sales Order", - "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 + "options": "Sales Order" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "users_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": "Users", - "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 + "label": "Users" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Project will be accessible on the website to these users", "fieldname": "users", "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": "Users", - "length": 0, - "no_copy": 0, - "options": "Project User", - "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 + "options": "Project User" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_milestones", - "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": "Tasks", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-flag", - "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": "tasks", - "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": "Tasks", - "length": 0, - "no_copy": 0, - "options": "Project Task", - "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": "copied_from", "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": "Copied From", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "section_break0", "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": "Notes", - "length": 0, - "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-list", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "fa fa-list" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "notes", "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Notes", - "length": 0, - "no_copy": 0, "oldfieldname": "notes", - "oldfieldtype": "Text Editor", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Text Editor" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "section_break_18", "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": "Start and End Dates", - "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 + "label": "Start and End Dates" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "actual_start_date", "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": "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "actual_time", "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": "Actual Time (in Hours)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_20", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "actual_end_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": "Actual End Date", - "length": 0, - "no_copy": 0, "oldfieldname": "act_completion_date", "oldfieldtype": "Date", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "project_details", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Costing and Billing", - "length": 0, - "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-money", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "fa fa-money" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "estimated_costing", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Estimated Cost", - "length": 0, - "no_copy": 0, "oldfieldname": "project_value", "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Company:company:default_currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "total_costing_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Total Costing Amount (via Timesheets)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "total_expense_claim", "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 Expense Claim (via Expense Claims)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_purchase_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 Purchase Cost (via Purchase Invoice)", - "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 + "read_only": 1 }, { - "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, "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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "remember_last_selected_value": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_28", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_sales_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Total Sales Amount (via Sales Order)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "total_billable_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Total Billable Amount (via Timesheets)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_billed_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Total Billed Amount (via Sales Invoices)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_consumed_material_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 Consumed Material Cost (via Stock Entry)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "cost_center", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Default Cost Center", - "length": 0, - "no_copy": 0, - "options": "Cost Center", - "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 + "options": "Cost Center" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "margin", "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": "Margin", - "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": "gross_margin", "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": "Gross Margin", - "length": 0, - "no_copy": 0, "oldfieldname": "gross_margin_value", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_37", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "per_gross_margin", "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Gross Margin %", - "length": 0, - "no_copy": 0, "oldfieldname": "per_gross_margin", "oldfieldtype": "Currency", - "options": "", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "monitor_progress", "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": "Monitor Progress", - "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 + "label": "Monitor Progress" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "collect_progress", "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": "Collect Progress", - "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 + "label": "Collect Progress" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "collect_progress", "fieldname": "holiday_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": "Holiday List", - "length": 0, - "no_copy": 0, - "options": "Holiday 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 + "options": "Holiday List" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.collect_progress == true", "fieldname": "frequency", "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": "Frequency To Collect Progress", - "length": 0, - "no_copy": 0, - "options": "Hourly\nTwice Daily\nDaily\nWeekly", - "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 + "options": "Hourly\nTwice Daily\nDaily\nWeekly" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Hourly\" && doc.collect_progress)", "fieldname": "from_time", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "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 + "label": "From Time" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Hourly\" && doc.collect_progress)", "fieldname": "to_time", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "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 + "label": "To Time" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Twice Daily\" && doc.collect_progress == true)\n\n", "fieldname": "first_email", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "First Email", - "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 + "label": "First Email" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Twice Daily\" && doc.collect_progress == true)", "fieldname": "second_email", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Second Email", - "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 + "label": "Second Email" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Daily\" && doc.collect_progress == true)", "fieldname": "daily_time_to_send", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Time to send", - "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 + "label": "Time to send" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)", "fieldname": "day_to_send", "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": "Day to Send", - "length": 0, - "no_copy": 0, - "options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday", - "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 + "options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)", "fieldname": "weekly_time_to_send", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Time to send", - "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 + "label": "Time to send" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_45", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "collect_progress", "description": "Message will sent to users to get their status on the project", "fieldname": "message", "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": "Message", - "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 + "label": "Message" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-puzzle-piece", "idx": 29, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, "max_attachments": 4, - "modified": "2019-02-18 17:56:04.789560", + "modified": "2019-06-25 16:14:43.887151", "modified_by": "Administrator", "module": "Projects", "name": "Project", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Projects User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, "report": 1, - "role": "All", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "All" }, { - "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": "Projects Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "search_fields": "customer, status, priority, is_active", "show_name_in_global_search": 1, "sort_order": "DESC", "timeline_field": "customer", - "track_changes": 0, - "track_seen": 1, - "track_views": 0 + "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 55a689259a..6176cf89b4 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -19,10 +19,6 @@ class Project(Document): return '{0}: {1}'.format(_(self.status), frappe.safe_decode(self.project_name)) def onload(self): - """Load project tasks for quick view""" - if not self.get('__unsaved') and not self.get("tasks"): - self.load_tasks() - self.set_onload('activity_summary', frappe.db.sql('''select activity_type, sum(hours) as total_hours from `tabTimesheet Detail` where project=%s and docstatus < 2 group by activity_type @@ -33,57 +29,19 @@ class Project(Document): def before_print(self): self.onload() - def load_tasks(self): - """Load `tasks` from the database""" - if frappe.flags.in_import: - return - project_task_custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task"}, "fieldname") - - self.tasks = [] - for task in self.get_tasks(): - task_map = { - "title": task.subject, - "status": task.status, - "start_date": task.exp_start_date, - "end_date": task.exp_end_date, - "description": task.description, - "task_id": task.name, - "task_weight": task.task_weight - } - - self.map_custom_fields(task, task_map, project_task_custom_fields) - - self.append("tasks", task_map) - - def get_tasks(self): - if self.name is None: - return {} - else: - filters = {"project": self.name} - - if self.get("deleted_task_list"): - filters.update({ - 'name': ("not in", self.deleted_task_list) - }) - - return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc, status asc") def validate(self): - self.validate_weights() - self.sync_tasks() - self.tasks = [] - self.load_tasks() if not self.is_new(): self.copy_from_template() - self.validate_dates() self.send_welcome_email() - self.update_percent_complete(from_validate=True) + self.update_costing() + self.update_percent_complete() def copy_from_template(self): ''' Copy tasks from template ''' - if self.project_template and not len(self.tasks or []): + if self.project_template and not frappe.db.get_all('Task', dict(project = self.name), limit=1): # has a template, and no loaded tasks, so lets create if not self.expected_start_date: @@ -108,104 +66,6 @@ class Project(Document): task_weight = task.task_weight )).insert() - # reload tasks after project - self.load_tasks() - - def validate_dates(self): - if self.tasks: - for d in self.tasks: - if self.expected_start_date: - if d.start_date and getdate(d.start_date) < getdate(self.expected_start_date): - frappe.throw(_("Start date of task {0} cannot be less than {1} expected start date {2}") - .format(d.title, self.name, self.expected_start_date)) - if d.end_date and getdate(d.end_date) < getdate(self.expected_start_date): - frappe.throw(_("End date of task {0} cannot be less than {1} expected start date {2}") - .format(d.title, self.name, self.expected_start_date)) - - if self.expected_end_date: - if d.start_date and getdate(d.start_date) > getdate(self.expected_end_date): - frappe.throw(_("Start date of task {0} cannot be greater than {1} expected end date {2}") - .format(d.title, self.name, self.expected_end_date)) - if d.end_date and getdate(d.end_date) > getdate(self.expected_end_date): - frappe.throw(_("End date of task {0} cannot be greater than {1} expected end date {2}") - .format(d.title, self.name, self.expected_end_date)) - - if self.expected_start_date and self.expected_end_date: - if getdate(self.expected_end_date) < getdate(self.expected_start_date): - frappe.throw(_("Expected End Date can not be less than Expected Start Date")) - - def validate_weights(self): - for task in self.tasks: - if task.task_weight is not None: - if task.task_weight < 0: - frappe.throw(_("Task weight cannot be negative")) - - def sync_tasks(self): - """sync tasks and remove table""" - if not hasattr(self, "deleted_task_list"): - self.set("deleted_task_list", []) - - if self.flags.dont_sync_tasks: return - task_names = [] - - existing_task_data = {} - - fields = ["title", "status", "start_date", "end_date", "description", "task_weight", "task_id"] - exclude_fieldtype = ["Button", "Column Break", - "Section Break", "Table", "Read Only", "Attach", "Attach Image", "Color", "Geolocation", "HTML", "Image"] - - custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task", - "fieldtype": ("not in", exclude_fieldtype)}, "fieldname") - - for d in custom_fields: - fields.append(d.fieldname) - - for d in frappe.get_all('Project Task', - fields = fields, - filters = {'parent': self.name}): - existing_task_data.setdefault(d.task_id, d) - - for t in self.tasks: - if t.task_id: - task = frappe.get_doc("Task", t.task_id) - else: - task = frappe.new_doc("Task") - task.project = self.name - - if not t.task_id or self.is_row_updated(t, existing_task_data, fields): - task.update({ - "subject": t.title, - "status": t.status, - "exp_start_date": t.start_date, - "exp_end_date": t.end_date, - "description": t.description, - "task_weight": t.task_weight - }) - - self.map_custom_fields(t, task, custom_fields) - - task.flags.ignore_links = True - task.flags.from_project = True - task.flags.ignore_feed = True - - if t.task_id: - task.update({ - "modified_by": frappe.session.user, - "modified": now() - }) - - task.run_method("validate") - task.db_update() - else: - task.save(ignore_permissions = True) - task_names.append(task.name) - else: - task_names.append(task.name) - - # delete - for t in frappe.get_all("Task", ["name"], {"project": self.name, "name": ("not in", task_names)}): - self.deleted_task_list.append(t.name) - def is_row_updated(self, row, existing_task_data, fields): if self.get("__islocal") or not existing_task_data: return True @@ -215,48 +75,43 @@ class Project(Document): if row.get(field) != d.get(field): return True - def map_custom_fields(self, source, target, custom_fields): - for field in custom_fields: - target.update({ - field.fieldname: source.get(field.fieldname) - }) - def update_project(self): + '''Called externally by Task''' self.update_percent_complete() self.update_costing() + self.db_update() def after_insert(self): self.copy_from_template() if self.sales_order: frappe.db.set_value("Sales Order", self.sales_order, "project", self.name) - def update_percent_complete(self, from_validate=False): - if not self.tasks: return - total = frappe.db.sql("""select count(name) from tabTask where project=%s""", self.name)[0][0] + def update_percent_complete(self): + total = frappe.db.count('Task', dict(project=self.name)) - if not total and self.percent_complete: + if not total: self.percent_complete = 0 + else: + if (self.percent_complete_method == "Task Completion" and total > 0) or ( + not self.percent_complete_method and total > 0): + completed = frappe.db.sql("""select count(name) from tabTask where + project=%s and status in ('Cancelled', 'Completed')""", self.name)[0][0] + self.percent_complete = flt(flt(completed) / total * 100, 2) - if (self.percent_complete_method == "Task Completion" and total > 0) or ( - not self.percent_complete_method and total > 0): - completed = frappe.db.sql("""select count(name) from tabTask where - project=%s and status in ('Cancelled', 'Completed')""", self.name)[0][0] - self.percent_complete = flt(flt(completed) / total * 100, 2) + if (self.percent_complete_method == "Task Progress" and total > 0): + progress = frappe.db.sql("""select sum(progress) from tabTask where + project=%s""", self.name)[0][0] + self.percent_complete = flt(flt(progress) / total, 2) - if (self.percent_complete_method == "Task Progress" and total > 0): - progress = frappe.db.sql("""select sum(progress) from tabTask where - project=%s""", self.name)[0][0] - self.percent_complete = flt(flt(progress) / total, 2) - - if (self.percent_complete_method == "Task Weight" and total > 0): - weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where - project=%s""", self.name)[0][0] - weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where - project=%s""", self.name, as_dict=1) - pct_complete = 0 - for row in weighted_progress: - pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum) - self.percent_complete = flt(flt(pct_complete), 2) + if (self.percent_complete_method == "Task Weight" and total > 0): + weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where + project=%s""", self.name)[0][0] + weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where + project=%s""", self.name, as_dict=1) + pct_complete = 0 + for row in weighted_progress: + pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum) + self.percent_complete = flt(flt(pct_complete), 2) # don't update status if it is cancelled if self.status == 'Cancelled': @@ -268,9 +123,6 @@ class Project(Document): else: self.status = "Open" - if not from_validate: - self.db_update() - def update_costing(self): from_time_sheet = frappe.db.sql("""select sum(costing_amount) as costing_amount, @@ -297,7 +149,6 @@ class Project(Document): self.update_sales_amount() self.update_billed_amount() self.calculate_gross_margin() - self.db_update() def calculate_gross_margin(self): expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim) @@ -348,57 +199,6 @@ class Project(Document): content=content.format(*messages)) user.welcome_email_sent = 1 - def on_update(self): - self.delete_task() - self.load_tasks() - self.update_project() - self.update_dependencies_on_duplicated_project() - - def delete_task(self): - if not self.get('deleted_task_list'): return - - for d in self.get('deleted_task_list'): - # unlink project - frappe.db.set_value('Task', d, 'project', '') - - self.deleted_task_list = [] - - def update_dependencies_on_duplicated_project(self): - if self.flags.dont_sync_tasks: return - if not self.copied_from: - self.copied_from = self.name - - if self.name != self.copied_from and self.get('__unsaved'): - # duplicated project - dependency_map = {} - for task in self.tasks: - _task = frappe.db.get_value( - 'Task', - {"subject": task.title, "project": self.copied_from}, - ['name', 'depends_on_tasks'], - as_dict=True - ) - - if _task is None: - continue - - name = _task.name - - dependency_map[task.title] = [x['subject'] for x in frappe.get_list( - 'Task Depends On', {"parent": name}, ['subject'])] - - for key, value in iteritems(dependency_map): - task_name = frappe.db.get_value('Task', {"subject": key, "project": self.name }) - - task_doc = frappe.get_doc('Task', task_name) - - for dt in value: - dt_name = frappe.db.get_value('Task', {"subject": dt, "project": self.name}) - task_doc.append('depends_on', {"task": dt_name}) - - task_doc.db_update() - - def get_timeline_data(doctype, name): '''Return timeline for attendance''' return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*) diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index beb1f130f5..06c62b62d2 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -19,18 +19,18 @@ class TestProject(unittest.TestCase): project = get_project('Test Project with Template') - project.load_tasks() + tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc') - task1 = project.tasks[0] - self.assertEqual(task1.title, 'Task 1') + task1 = tasks[0] + self.assertEqual(task1.subject, 'Task 1') self.assertEqual(task1.description, 'Task 1 description') - self.assertEqual(getdate(task1.start_date), getdate('2019-01-01')) - self.assertEqual(getdate(task1.end_date), getdate('2019-01-04')) + self.assertEqual(getdate(task1.exp_start_date), getdate('2019-01-01')) + self.assertEqual(getdate(task1.exp_end_date), getdate('2019-01-04')) - self.assertEqual(len(project.tasks), 4) - task4 = project.tasks[3] - self.assertEqual(task4.title, 'Task 4') - self.assertEqual(getdate(task4.end_date), getdate('2019-01-06')) + self.assertEqual(len(tasks), 4) + task4 = tasks[3] + self.assertEqual(task4.subject, 'Task 4') + self.assertEqual(getdate(task4.exp_end_date), getdate('2019-01-06')) def get_project(name): template = get_project_template() diff --git a/erpnext/projects/doctype/project/test_records.json b/erpnext/projects/doctype/project/test_records.json index 9379c22b5c..567f359b50 100644 --- a/erpnext/projects/doctype/project/test_records.json +++ b/erpnext/projects/doctype/project/test_records.json @@ -1,12 +1,6 @@ [ { "project_name": "_Test Project", - "status": "Open", - "tasks":[ - { - "title": "_Test Task", - "status": "Open" - } - ] + "status": "Open" } ] \ No newline at end of file diff --git a/erpnext/projects/doctype/project_task/__init__.py b/erpnext/projects/doctype/project_task/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/projects/doctype/project_task/project_task.json b/erpnext/projects/doctype/project_task/project_task.json deleted file mode 100644 index e26c191e0b..0000000000 --- a/erpnext/projects/doctype/project_task/project_task.json +++ /dev/null @@ -1,430 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2015-02-22 11:15:28.201059", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Other", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "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": 3, - "default": "Open", - "fieldname": "status", - "fieldtype": "Select", - "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": "Status", - "length": 0, - "no_copy": 1, - "options": "Open\nWorking\nPending Review\nOverdue\nCompleted\nCancelled", - "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, - "depends_on": "task_id", - "fieldname": "edit_task", - "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": "View Task", - "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": "edit_timesheet", - "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": "View Timesheet", - "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": "make_timesheet", - "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": "Make Timesheet", - "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_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "start_date", - "fieldtype": "Date", - "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": "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": 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": 2, - "default": "", - "fieldname": "end_date", - "fieldtype": "Date", - "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": "End 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": 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": "task_weight", - "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": "Weight", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "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": "task_id", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Task ID", - "length": 0, - "no_copy": 1, - "options": "Task", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "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-19 12:30:52.648868", - "modified_by": "Administrator", - "module": "Projects", - "name": "Project Task", - "name_case": "", - "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": 0, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/projects/doctype/project_task/project_task.py b/erpnext/projects/doctype/project_task/project_task.py deleted file mode 100644 index 5f9d8d7622..0000000000 --- a/erpnext/projects/doctype/project_task/project_task.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class ProjectTask(Document): - pass diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index d8fc199ec2..50557f1551 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -158,12 +158,6 @@ class Task(NestedSet): if check_if_child_exists(self.name): throw(_("Child Task exists for this Task. You can not delete this Task.")) - if self.project: - tasks = frappe.get_doc('Project', self.project).tasks - for task in tasks: - if task.get('task_id') == self.name: - frappe.delete_doc('Project Task', task.name) - self.update_nsm_model() def update_status(self): diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 8ad3bf0607..6c0b02dd48 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -547,12 +547,6 @@ def make_project(source_name, target_doc=None): "base_grand_total" : "estimated_costing", } }, - "Sales Order Item": { - "doctype": "Project Task", - "field_map": { - "item_code": "title", - }, - } }, target_doc, postprocess) return doc From 6a7969117f1ec438f25ec5f8bfbbec10a04ef01d Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 8 Jul 2019 10:40:40 +0530 Subject: [PATCH 37/83] fix(bom): escape name with wildcard character (#18164) --- erpnext/controllers/queries.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index d74bc0ea18..47c9f0a4ce 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -206,10 +206,11 @@ def bom(doctype, txt, searchfield, start, page_len, filters): if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), idx desc, name limit %(start)s, %(page_len)s """.format( - fcond=get_filters_cond(doctype, filters, conditions), + fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'), mcond=get_match_cond(doctype), - key=searchfield), { - 'txt': '%' + txt + '%', + key=frappe.db.escape(searchfield)), + { + 'txt': "%"+frappe.db.escape(txt)+"%", '_txt': txt.replace("%", ""), 'start': start or 0, 'page_len': page_len or 20 From de13faf19a123998fa9dd1578b4d2d3d966faddc Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 9 Jul 2019 11:53:31 +0530 Subject: [PATCH 38/83] fix: sending sms from quotation --- erpnext/public/js/sms_manager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/sms_manager.js b/erpnext/public/js/sms_manager.js index 6ce8bb1150..a7003a272d 100644 --- a/erpnext/public/js/sms_manager.js +++ b/erpnext/public/js/sms_manager.js @@ -20,8 +20,10 @@ erpnext.SMSManager = function SMSManager(doc) { 'Purchase Receipt' : 'Items has been received against purchase receipt: ' + doc.name } - if (in_list(['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype)) + if (in_list(['Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype)) this.show(doc.contact_person, 'Customer', doc.customer, '', default_msg[doc.doctype]); + else if (in_list(['Quotation'], doc.doctype)) + this.show(doc.contact_person, 'Customer', doc.party_name, '', default_msg[doc.doctype]); else if (in_list(['Purchase Order', 'Purchase Receipt'], doc.doctype)) this.show(doc.contact_person, 'Supplier', doc.supplier, '', default_msg[doc.doctype]); else if (doc.doctype == 'Lead') From 890ea195f3cc6487ab0809926c3a3cbea2837d12 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Tue, 9 Jul 2019 16:56:56 +0530 Subject: [PATCH 39/83] fix: Tree filter fixes for erpnext dimensions --- erpnext/public/js/utils/dimension_tree_filter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index a9122d8dff..549f95e039 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -12,7 +12,7 @@ erpnext.dimension_filters = erpnext.get_dimension_filters(); erpnext.doctypes_with_dimensions.forEach((doctype) => { frappe.ui.form.on(doctype, { onload: function(frm) { - dimension_filters.then((dimensions) => { + erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { frappe.model.with_doctype(dimension['document_type'], () => { if (frappe.meta.has_field(dimension['document_type'], 'is_group')) { From e9dd9b842e289a9683743a92b8974cba2c968df8 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 9 Jul 2019 17:54:00 +0530 Subject: [PATCH 40/83] Update erpnext/public/js/sms_manager.js Co-Authored-By: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/public/js/sms_manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/sms_manager.js b/erpnext/public/js/sms_manager.js index a7003a272d..a058da23ac 100644 --- a/erpnext/public/js/sms_manager.js +++ b/erpnext/public/js/sms_manager.js @@ -22,7 +22,7 @@ erpnext.SMSManager = function SMSManager(doc) { if (in_list(['Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype)) this.show(doc.contact_person, 'Customer', doc.customer, '', default_msg[doc.doctype]); - else if (in_list(['Quotation'], doc.doctype)) + else if (doc.doctype === 'Quotation') this.show(doc.contact_person, 'Customer', doc.party_name, '', default_msg[doc.doctype]); else if (in_list(['Purchase Order', 'Purchase Receipt'], doc.doctype)) this.show(doc.contact_person, 'Supplier', doc.supplier, '', default_msg[doc.doctype]); From 838697261d1d8aa1968b724f4e3168b5d3364295 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Wed, 10 Jul 2019 10:39:43 +0530 Subject: [PATCH 41/83] fix: Call popup avatar Avoid getting session users image if customer doc has no image --- erpnext/public/js/call_popup/call_popup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index 91dfe809a4..89657a1837 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -109,7 +109,7 @@ class CallPopup { }); wrapper.append(`
- ${frappe.avatar(null, 'avatar-xl', contact.name, contact.image)} + ${frappe.avatar(null, 'avatar-xl', contact.name, contact.image || '')}
${contact_name}
${contact.mobile_no || ''}
From 3d28c065dbbd0f8e4a25b2f5e579a3a26d24f49d Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 10 Jul 2019 17:03:23 +0530 Subject: [PATCH 42/83] fix(exchange-rate-revaluation): change create to view button on creation of journal entry (#18201) --- .../exchange_rate_revaluation.js | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js index dad75b4ba1..0d5456ece6 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js @@ -21,9 +21,29 @@ frappe.ui.form.on('Exchange Rate Revaluation', { refresh: function(frm) { if(frm.doc.docstatus==1) { - frm.add_custom_button(__('Create Journal Entry'), function() { - return frm.events.make_jv(frm); - }); + frappe.db.get_value("Journal Entry Account", { + 'reference_type': 'Exchange Rate Revaluation', + 'reference_name': frm.doc.name, + 'docstatus': 1 + }, "sum(debit) as sum", (r) =>{ + let total_amt = 0; + frm.doc.accounts.forEach(d=> { + total_amt = total_amt + d['new_balance_in_base_currency']; + }); + if(total_amt === r.sum) { + frm.add_custom_button(__("Journal Entry"), function(){ + frappe.route_options = { + 'reference_type': 'Exchange Rate Revaluation', + 'reference_name': frm.doc.name + }; + frappe.set_route("List", "Journal Entry"); + }, __("View")); + } else { + frm.add_custom_button(__('Create Journal Entry'), function() { + return frm.events.make_jv(frm); + }); + } + }, 'Journal Entry'); } }, From 058787d2044b83c8a12885e1879b711b388f570f Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 10 Jul 2019 17:03:48 +0530 Subject: [PATCH 43/83] fix: error report for item price (#18212) --- erpnext/stock/doctype/item_price/item_price.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index d182290427..30675b54b3 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -31,13 +31,16 @@ class ItemPrice(Document): frappe.throw(_("Valid From Date must be lesser than Valid Upto Date.")) def update_price_list_details(self): - self.buying, self.selling, self.currency = \ - frappe.db.get_value("Price List", - {"name": self.price_list, "enabled": 1}, - ["buying", "selling", "currency"]) + if self.price_list: + self.buying, self.selling, self.currency = \ + frappe.db.get_value("Price List", + {"name": self.price_list, "enabled": 1}, + ["buying", "selling", "currency"]) def update_item_details(self): - self.item_name, self.item_description = frappe.db.get_value("Item",self.item_code,["item_name", "description"]) + if self.item_code: + self.item_name, self.item_description = frappe.db.get_value("Item", + self.item_code,["item_name", "description"]) def check_duplicates(self): conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s" From 61d8088be406822d224aec1399cfce3d9d3fd092 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Wed, 10 Jul 2019 17:05:25 +0530 Subject: [PATCH 44/83] fix: Return fieldtype so that the client-side can format chart values (#18211) --- .../profit_and_loss_statement/profit_and_loss_statement.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index 48d7361fe0..ac11868cab 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -93,4 +93,6 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss): else: chart["type"] = "line" + chart["fieldtype"] = "Currency" + return chart \ No newline at end of file From 59c5c3c347b2fd14adb7d39f4165ae50dc61d3a9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 10 Jul 2019 17:05:49 +0530 Subject: [PATCH 45/83] fix: Make material request against SO only for pending qty (#18216) --- .../selling/doctype/sales_order/sales_order.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 6c0b02dd48..e9b310eb30 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -492,13 +492,27 @@ def close_or_unclose_sales_orders(names, status): frappe.local.message_log = [] +def get_requested_item_qty(sales_order): + return frappe._dict(frappe.db.sql(""" + select sales_order_item, sum(stock_qty) + from `tabMaterial Request Item` + where docstatus = 1 + and sales_order = %s + group by sales_order_item + """, sales_order)) + @frappe.whitelist() def make_material_request(source_name, target_doc=None): + requested_item_qty = get_requested_item_qty(source_name) + def postprocess(source, doc): doc.material_request_type = "Purchase" def update_item(source, target, source_parent): target.project = source_parent.project + target.qty = source.stock_qty - requested_item_qty.get(source.name, 0) + target.conversion_factor = 1 + target.stock_qty = source.stock_qty - requested_item_qty.get(source.name, 0) doc = get_mapped_doc("Sales Order", source_name, { "Sales Order": { @@ -523,7 +537,7 @@ def make_material_request(source_name, target_doc=None): "stock_uom": "uom", "stock_qty": "qty" }, - "condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code), + "condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code) and doc.stock_qty > requested_item_qty.get(doc.name, 0), "postprocess": update_item } }, target_doc, postprocess) From e7bb54ed767fc429027020f2e94c4a0a827ceeca Mon Sep 17 00:00:00 2001 From: Himanshu Date: Wed, 10 Jul 2019 11:45:59 +0000 Subject: [PATCH 46/83] feat(Issue): Settings to stop SLA tracking (#18112) * fix: fix msgprint when creating new issues * fix: use getdate * fix: do not self save * fix: test case * fix: error due to comparing date and str * fix: creation time for replicated issue * feat: sla in support settings * fix: remove sla settings * feat: enable check in sla * fix: throw if sla is disabled * fix: enable sla --- erpnext/config/support.py | 11 + .../doctype/customer/customer_dashboard.py | 4 - erpnext/support/doctype/issue/issue.js | 23 +- erpnext/support/doctype/issue/issue.json | 7 +- erpnext/support/doctype/issue/issue.py | 30 +- erpnext/support/doctype/issue/test_issue.py | 1 + .../service_level_agreement.json | 9 +- .../service_level_agreement.py | 24 +- .../test_service_level_agreement.py | 2 + .../support_settings/support_settings.json | 515 ++---------------- 10 files changed, 138 insertions(+), 488 deletions(-) diff --git a/erpnext/config/support.py b/erpnext/config/support.py index 0301bb3e19..36b4214196 100644 --- a/erpnext/config/support.py +++ b/erpnext/config/support.py @@ -97,4 +97,15 @@ def get_data(): }, ] }, + { + "label": _("Settings"), + "icon": "fa fa-list", + "items": [ + { + "type": "doctype", + "name": "Support Settings", + "label": _("Support Settings"), + }, + ] + }, ] \ 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 87ca6a76bc..8e790bf9ce 100644 --- a/erpnext/selling/doctype/customer/customer_dashboard.py +++ b/erpnext/selling/doctype/customer/customer_dashboard.py @@ -25,10 +25,6 @@ def get_data(): 'label': _('Orders'), 'items': ['Sales Order', 'Delivery Note', 'Sales Invoice'] }, - { - 'label': _('Service Level Agreement'), - 'items': ['Service Level Agreement'] - }, { 'label': _('Payments'), 'items': ['Payment Entry'] diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 1a272d1bc4..9d93f37af7 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -129,8 +129,15 @@ frappe.ui.form.on("Issue", { function set_time_to_resolve_and_response(frm) { frm.dashboard.clear_headline(); - var time_to_respond = get_time_left(frm.doc.response_by, frm.doc.agreement_fulfilled); - var time_to_resolve = get_time_left(frm.doc.resolution_by, frm.doc.agreement_fulfilled); + var time_to_respond = get_status(frm.doc.response_by_variance); + if (!frm.doc.first_responded_on && frm.doc.agreement_fulfilled === "Ongoing") { + time_to_respond = get_time_left(frm.doc.response_by, frm.doc.agreement_fulfilled); + } + + var time_to_resolve = get_status(frm.doc.resolution_by_variance); + if (!frm.doc.resolution_date && frm.doc.agreement_fulfilled === "Ongoing") { + time_to_resolve = get_time_left(frm.doc.response_by, frm.doc.agreement_fulfilled); + } frm.dashboard.set_headline_alert( '
' + @@ -146,7 +153,15 @@ function set_time_to_resolve_and_response(frm) { function get_time_left(timestamp, agreement_fulfilled) { const diff = moment(timestamp).diff(moment()); - const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : moment(0, 'seconds').format('HH:mm'); - let indicator = (diff_display == '00:00' && agreement_fulfilled != "Fulfilled") ? "red" : "green"; + const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : "Failed"; + let indicator = (diff_display == 'Failed' && agreement_fulfilled != "Fulfilled") ? "red" : "green"; return {"diff_display": diff_display, "indicator": indicator}; } + +function get_status(variance) { + if (variance > 0) { + return {"diff_display": "Fulfilled", "indicator": "green"}; + } else { + return {"diff_display": "Failed", "indicator": "red"}; + } +} \ No newline at end of file diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index b7c4166f6c..72153dcdea 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -113,6 +113,7 @@ "search_index": 1 }, { + "default": "Medium", "fieldname": "priority", "fieldtype": "Link", "in_standard_filter": 1, @@ -143,7 +144,6 @@ }, { "collapsible": 1, - "depends_on": "eval: doc.service_level_agreement", "fieldname": "service_level_section", "fieldtype": "Section Break", "label": "Service Level" @@ -314,6 +314,7 @@ }, { "default": "Ongoing", + "depends_on": "eval: doc.service_level_agreement", "fieldname": "agreement_fulfilled", "fieldtype": "Select", "label": "Service Level Agreement Fulfilled", @@ -321,6 +322,7 @@ "read_only": 1 }, { + "depends_on": "eval: doc.service_level_agreement", "description": "in hours", "fieldname": "response_by_variance", "fieldtype": "Float", @@ -328,6 +330,7 @@ "read_only": 1 }, { + "depends_on": "eval: doc.service_level_agreement", "description": "in hours", "fieldname": "resolution_by_variance", "fieldtype": "Float", @@ -337,7 +340,7 @@ ], "icon": "fa fa-ticket", "idx": 7, - "modified": "2019-06-27 15:19:00.771333", + "modified": "2019-06-30 13:19:38.215525", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index ad1c263250..226676fec3 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -92,7 +92,6 @@ class Issue(Document): self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime()), 2) self.agreement_fulfilled = "Fulfilled" if self.response_by_variance > 0 and self.resolution_by_variance > 0 else "Failed" - self.save(ignore_permissions=True) def create_communication(self): communication = frappe.new_doc("Communication") @@ -118,6 +117,17 @@ class Issue(Document): replicated_issue = deepcopy(self) replicated_issue.subject = subject + replicated_issue.creation = now_datetime() + + # Reset SLA + if replicated_issue.service_level_agreement: + replicated_issue.service_level_agreement = None + replicated_issue.agreement_fulfilled = "Ongoing" + replicated_issue.response_by = None + replicated_issue.response_by_variance = None + replicated_issue.resolution_by = None + replicated_issue.resolution_by_variance = None + frappe.get_doc(replicated_issue).insert() # Replicate linked Communications @@ -136,7 +146,8 @@ class Issue(Document): return replicated_issue.name def before_insert(self): - self.set_response_and_resolution_time() + if frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): + self.set_response_and_resolution_time() def set_response_and_resolution_time(self, priority=None, service_level_agreement=None): service_level_agreement = get_active_service_level_agreement_for(priority=priority, @@ -171,13 +182,16 @@ class Issue(Document): self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime())) def change_service_level_agreement_and_priority(self): - if not self.priority == frappe.db.get_value("Issue", self.name, "priority"): - self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) - frappe.msgprint("Priority has been updated.") + if self.service_level_agreement and frappe.db.exists("Issue", self.name) and \ + frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): - if not self.service_level_agreement == frappe.db.get_value("Issue", self.name, "service_level_agreement"): - self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) - frappe.msgprint("Service Level Agreement has been updated.") + if not self.priority == frappe.db.get_value("Issue", self.name, "priority"): + self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) + frappe.msgprint(_("Priority has been changed to {0}.").format(self.priority)) + + if not self.service_level_agreement == frappe.db.get_value("Issue", self.name, "service_level_agreement"): + self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) + frappe.msgprint(_("Service Level Agreement has been changed to {0}.").format(self.service_level_agreement)) def get_expected_time_for(parameter, service_level, start_date_time): current_date_time = start_date_time diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 75d70b1318..eb1736e6c9 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -11,6 +11,7 @@ from datetime import timedelta class TestIssue(unittest.TestCase): def test_response_time_and_resolution_time_based_on_different_sla(self): + frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) create_service_level_agreements_for_issues() creation = datetime.datetime(2019, 3, 4, 12, 0) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index f91a80c60d..9a83ca7ac0 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -5,6 +5,7 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "enable", "service_level", "default_service_level_agreement", "holiday_list", @@ -149,9 +150,15 @@ "in_standard_filter": 1, "label": "Entity Type", "options": "\nCustomer\nCustomer Group\nTerritory" + }, + { + "default": "1", + "fieldname": "enable", + "fieldtype": "Check", + "label": "Enable" } ], - "modified": "2019-06-20 18:04:14.293378", + "modified": "2019-07-09 17:22:16.402939", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index 82c0ffb65c..a399c58b16 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -6,19 +6,23 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ +from frappe.utils import getdate class ServiceLevelAgreement(Document): def validate(self): + if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): + frappe.throw(_("Service Level Agreement tracking is not enabled.")) + if self.default_service_level_agreement: if frappe.db.exists("Service Level Agreement", {"default_service_level_agreement": "1", "name": ["!=", self.name]}): frappe.throw(_("A Default Service Level Agreement already exists.")) else: if self.start_date and self.end_date: - if self.start_date >= self.end_date: + if getdate(self.start_date) >= getdate(self.end_date): frappe.throw(_("Start Date of Agreement can't be greater than or equal to End Date.")) - if self.end_date < frappe.utils.getdate(): + if getdate(self.end_date) < getdate(frappe.utils.getdate()): frappe.throw(_("End Date of Agreement can't be less than today.")) if self.entity_type and self.entity: @@ -44,12 +48,16 @@ def check_agreement_status(): for service_level_agreement in service_level_agreements: doc = frappe.get_doc("Service Level Agreement", service_level_agreement.name) - if doc.end_date and doc.end_date < frappe.utils.getdate(): + if doc.end_date and getdate(doc.end_date) < getdate(frappe.utils.getdate()): frappe.db.set_value("Service Level Agreement", service_level_agreement.name, "active", 0) def get_active_service_level_agreement_for(priority, customer=None, service_level_agreement=None): + if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): + return + filters = [ ["Service Level Agreement", "active", "=", 1], + ["Service Level Agreement", "enable", "=", 1] ] if priority: @@ -80,6 +88,14 @@ def get_customer_territory(customer): @frappe.whitelist() def get_service_level_agreement_filters(name, customer=None): + if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): + return + + filters = [ + ["Service Level Agreement", "active", "=", 1], + ["Service Level Agreement", "enable", "=", 1] + ] + if not customer: or_filters = [ ["Service Level Agreement", "default_service_level_agreement", "=", 1] @@ -93,5 +109,5 @@ def get_service_level_agreement_filters(name, customer=None): return { "priority": [priority.priority for priority in frappe.get_list("Service Level Priority", filters={"parent": name}, fields=["priority"])], - "service_level_agreements": [d.name for d in frappe.get_list("Service Level Agreement", or_filters=or_filters)] + "service_level_agreements": [d.name for d in frappe.get_list("Service Level Agreement", filters=filters, or_filters=or_filters)] } \ No newline at end of file diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 6aa5394192..68b82d10ec 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -10,6 +10,8 @@ from erpnext.support.doctype.service_level.test_service_level import create_serv class TestServiceLevelAgreement(unittest.TestCase): def test_service_level_agreement(self): + frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) + create_service_level_for_sla() # Default Service Level Agreement diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json index 2b79107269..2dced15d4e 100644 --- a/erpnext/support/doctype/support_settings/support_settings.json +++ b/erpnext/support/doctype/support_settings/support_settings.json @@ -1,560 +1,145 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, "creation": "2017-02-17 13:07:35.686409", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "sb_00", + "track_service_level_agreement", + "issues_sb", + "close_issue_after_days", + "portal_sb", + "get_started_sections", + "show_latest_forum_posts", + "forum_sb", + "forum_url", + "get_latest_query", + "response_key_list", + "column_break_10", + "post_title_key", + "post_description_key", + "post_route_key", + "post_route_string", + "search_apis_sb", + "search_apis" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "issues_sb", "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": "Issues", - "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 + "label": "Issues" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "7", "description": "Auto close Issue after 7 days", "fieldname": "close_issue_after_days", "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": "Close Issue After Days", - "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 + "label": "Close Issue After Days" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "portal_sb", "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": "Support Portal", - "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 + "label": "Support Portal" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "get_started_sections", "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Get Started Sections", - "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 + "label": "Get Started Sections" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "show_latest_forum_posts", "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 Latest Forum Posts", - "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 + "label": "Show Latest Forum Posts" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "show_latest_forum_posts", "fieldname": "forum_sb", "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": "Forum Posts", - "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 + "label": "Forum Posts" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "forum_url", "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": "Forum URL", - "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 + "label": "Forum URL" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "get_latest_query", "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": "Get Latest Query", - "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 + "label": "Get Latest Query" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "response_key_list", "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": "Response Key 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, - "unique": 0 + "label": "Response Key List" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_10", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "post_title_key", "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": "Post Title Key", - "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 + "label": "Post Title Key" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "post_description_key", "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": "Post Description Key", - "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 + "label": "Post Description Key" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "post_route_key", "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": "Post Route Key", - "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 + "label": "Post Route Key" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "post_route_string", "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": "Post Route String", - "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 + "label": "Post Route String" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "search_apis_sb", "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": "Search APIs", - "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 + "label": "Search APIs" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "search_apis", "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": "Search APIs", - "length": 0, - "no_copy": 0, - "options": "Support Search Source", - "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 + "options": "Support Search Source" + }, + { + "fieldname": "sb_00", + "fieldtype": "Section Break", + "label": "Service Level Agreements" + }, + { + "default": "0", + "fieldname": "track_service_level_agreement", + "fieldtype": "Check", + "label": "Track Service Level Agreement" } ], - "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-05-17 02:11:33.462444", + "modified": "2019-07-09 17:11:38.216732", "modified_by": "Administrator", "module": "Support", "name": "Support Settings", - "name_case": "", "owner": "Administrator", "permissions": [ { - "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": "System 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 + "track_changes": 1 } \ No newline at end of file From ab46f0f5bb8096b3cc5b805d8ada0f3751a3f926 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 10 Jul 2019 18:49:41 +0530 Subject: [PATCH 47/83] fix: Add module map for breadcrumbs --- erpnext/public/js/conf.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/public/js/conf.js b/erpnext/public/js/conf.js index ec71df3351..622e1abd62 100644 --- a/erpnext/public/js/conf.js +++ b/erpnext/public/js/conf.js @@ -52,3 +52,11 @@ $.extend(frappe.breadcrumbs.preferred, { "Sales Partner": "Selling", "Brand": "Selling" }); + +$.extend(frappe.breadcrumbs.module_map, { + 'ERPNext Integrations': 'Integrations', + 'Geo': 'Settings', + 'Accounts': 'Accounting', + 'Portal': 'Website', + 'Utilities': 'Settings' +}) From a7a6987c4213079428e68f9aedd9e9117ddcb768 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 10 Jul 2019 19:22:16 +0530 Subject: [PATCH 48/83] fix: Add Shopping Cart and Contacts --- erpnext/public/js/conf.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/conf.js b/erpnext/public/js/conf.js index 622e1abd62..dfee2b5578 100644 --- a/erpnext/public/js/conf.js +++ b/erpnext/public/js/conf.js @@ -58,5 +58,7 @@ $.extend(frappe.breadcrumbs.module_map, { 'Geo': 'Settings', 'Accounts': 'Accounting', 'Portal': 'Website', - 'Utilities': 'Settings' + 'Utilities': 'Settings', + 'Shopping Cart': 'Website', + 'Contacts': 'CRM' }) From d749f20f6317cf421af665af89b307b438c85cc8 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 10 Jul 2019 19:29:39 +0530 Subject: [PATCH 49/83] fix: installation note (#18232) --- erpnext/selling/doctype/installation_note/installation_note.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/installation_note/installation_note.js b/erpnext/selling/doctype/installation_note/installation_note.js index a8d9ae8a4e..7fd0877d11 100644 --- a/erpnext/selling/doctype/installation_note/installation_note.js +++ b/erpnext/selling/doctype/installation_note/installation_note.js @@ -3,7 +3,7 @@ frappe.ui.form.on('Installation Note', { setup: function(frm) { - frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'} + frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'} frm.set_query('customer_address', erpnext.queries.address_query); frm.set_query('contact_person', erpnext.queries.contact_query); frm.set_query('customer', erpnext.queries.customer); From 7837fbe231e2c442f21f0c6bcaac26a4800c4f34 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Wed, 10 Jul 2019 19:39:40 +0530 Subject: [PATCH 50/83] style: Add missing semicolon --- erpnext/public/js/conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/conf.js b/erpnext/public/js/conf.js index dfee2b5578..095e744926 100644 --- a/erpnext/public/js/conf.js +++ b/erpnext/public/js/conf.js @@ -61,4 +61,4 @@ $.extend(frappe.breadcrumbs.module_map, { 'Utilities': 'Settings', 'Shopping Cart': 'Website', 'Contacts': 'CRM' -}) +}); From f2f1e81a3f60999bf3dddf308ba8889defb99474 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 10 Jul 2019 20:28:52 +0530 Subject: [PATCH 51/83] feat: added chart to cash-flow report --- .../accounts/report/cash_flow/cash_flow.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index 75d99e75de..626d18514e 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -69,7 +69,9 @@ def execute(filters=None): add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency) columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company) - return columns, data + chart = get_chart_data(columns, data) + + return columns, data, None, chart def get_cash_flow_accounts(): operation_accounts = { @@ -171,4 +173,19 @@ def add_total_row_account(out, data, label, period_list, currency, consolidated total_row["total"] += row["total"] out.append(total_row) - out.append({}) \ No newline at end of file + out.append({}) + +def get_chart_data(columns, data): + labels = [d.get("label") for d in columns[2:]] + datasets = [{'name':account.get('account').replace("'", ""), 'values': [account.get('total')]} for account in data if account.get('parent_account') == None and account.get('currency')] + datasets = datasets[:-1] + + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + }, + "type": "bar" + } + + return chart \ No newline at end of file From 285701d9f104400ef90c8e7ef8e3513b6c3dee21 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Wed, 10 Jul 2019 22:22:14 +0530 Subject: [PATCH 52/83] fix: Add missing Cost Center filter in cash flow statement --- .../accounts/report/cash_flow/cash_flow.js | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index 0422111093..03940f4b24 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -8,17 +8,19 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { // The last item in the array is the definition for Presentation Currency // filter. It won't be used in cash flow for now so we pop it. Please take // of this if you are working here. - frappe.query_reports["Cash Flow"]["filters"].pop(); - frappe.query_reports["Cash Flow"]["filters"].push({ - "fieldname": "accumulated_values", - "label": __("Accumulated Values"), - "fieldtype": "Check" - }); + frappe.query_reports["Cash Flow"]["filters"].splice(5, 1); - frappe.query_reports["Cash Flow"]["filters"].push({ - "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), - "fieldtype": "Check" - }); + frappe.query_reports["Cash Flow"]["filters"].push( + { + "fieldname": "accumulated_values", + "label": __("Accumulated Values"), + "fieldtype": "Check" + }, + { + "fieldname": "include_default_book_entries", + "label": __("Include Default Book Entries"), + "fieldtype": "Check" + } + ); }); \ No newline at end of file From 4870e95b834c72664b5f138ec91e806968c0129a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 11 Jul 2019 10:03:35 +0530 Subject: [PATCH 53/83] fix: po, rate set as zero against the item (#18242) --- erpnext/public/js/controllers/buying.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 824b8d98d2..750900e3be 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -141,6 +141,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ price_list_rate: function(doc, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); + frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]); let item_rate = item.price_list_rate; @@ -154,6 +155,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ if (item.discount_amount) { item.rate = flt((item.price_list_rate) - (item.discount_amount), precision('rate', item)); + } else { + item.rate = item_rate; } this.calculate_taxes_and_totals(); From bfcc84789f0e170e6dd1c11453fd8c05b2aec2e3 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 11 Jul 2019 10:04:46 +0530 Subject: [PATCH 54/83] fix(sales-invoice): get items from quotation (#18237) --- .../accounts/doctype/sales_invoice/sales_invoice.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 07494a27d6..c3c3e2643e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -187,9 +187,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte method: "erpnext.selling.doctype.quotation.quotation.make_sales_invoice", source_doctype: "Quotation", target: me.frm, - setters: { - customer: me.frm.doc.customer || undefined, - }, + setters: [{ + fieldtype: 'Link', + label: __('Customer'), + options: 'Customer', + fieldname: 'party_name', + default: me.frm.doc.customer, + }], get_query_filters: { docstatus: 1, status: ["!=", "Lost"], From c6c6b05522bc3f3b97276df3ad7f820684817397 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 11 Jul 2019 10:42:37 +0530 Subject: [PATCH 55/83] fix: update_due_date_in_gle patch (#18248) --- erpnext/patches/v12_0/update_due_date_in_gle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v12_0/update_due_date_in_gle.py b/erpnext/patches/v12_0/update_due_date_in_gle.py index 4c47a82dcc..34848725ce 100644 --- a/erpnext/patches/v12_0/update_due_date_in_gle.py +++ b/erpnext/patches/v12_0/update_due_date_in_gle.py @@ -13,5 +13,5 @@ def execute(): WHERE `tabGL Entry`.voucher_no = `tab{doctype}`.name and `tabGL Entry`.party is not null and `tabGL Entry`.voucher_type in ('Sales Invoice', 'Purchase Invoice', 'Journal Entry') - and account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable') )""" #nosec + and `tabGL Entry`.account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable'))""" #nosec .format(doctype=doctype)) From bc2ff785f14b144d531568abb0cdac6c594ff4c7 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 11 Jul 2019 10:43:40 +0530 Subject: [PATCH 56/83] fix: Restore "allow delivery" condition checks (#18246) * fix: Remove `allow_delivery` flag - User was not able to create delivery note because allow_delivery flag was false and it never used to get updated * fix: Restore allow_delivery condition checks --- erpnext/selling/doctype/sales_order/sales_order.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 26ca7c6e22..39dda92e3e 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -107,7 +107,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( refresh: function(doc, dt, dn) { var me = this; this._super(); - var allow_delivery = false; + let allow_delivery = false; if(doc.docstatus==1) { if(this.frm.has_perm("submit")) { @@ -132,6 +132,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( if(doc.status !== 'Closed') { if(doc.status !== 'On Hold') { + allow_delivery = this.frm.doc.items.some(item => item.delivered_by_supplier === 0 && item.qty > flt(item.delivered_qty)) + if (this.frm.has_perm("submit")) { if(flt(doc.per_delivered, 6) < 100 || flt(doc.per_billed) < 100) { // hold From 523d0ab0d4f0ca700967237b3a6894b9f7de1dc5 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 11 Jul 2019 13:24:28 +0530 Subject: [PATCH 57/83] fix: resolution by indicator --- erpnext/support/doctype/issue/issue.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 9d93f37af7..2d9650c5a4 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -136,7 +136,7 @@ function set_time_to_resolve_and_response(frm) { var time_to_resolve = get_status(frm.doc.resolution_by_variance); if (!frm.doc.resolution_date && frm.doc.agreement_fulfilled === "Ongoing") { - time_to_resolve = get_time_left(frm.doc.response_by, frm.doc.agreement_fulfilled); + time_to_resolve = get_time_left(frm.doc.resolution_by, frm.doc.agreement_fulfilled); } frm.dashboard.set_headline_alert( From 870cbad5b2d1eca579806ca16227a6cff7a1fe48 Mon Sep 17 00:00:00 2001 From: Jignesh Greycube Date: Thu, 11 Jul 2019 13:56:43 +0530 Subject: [PATCH 58/83] fix: navigation link updated for Item Shortage Report Item Shortage Report navigation link was incorrect , hence updated. --- erpnext/config/stock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py index 84aa8474d3..f5e48b3b14 100644 --- a/erpnext/config/stock.py +++ b/erpnext/config/stock.py @@ -281,9 +281,9 @@ def get_data(): }, { "type": "report", + "is_query_report": True, "name": "Item Shortage Report", - "route": "#Report/Bin/Item Shortage Report", - "doctype": "Purchase Receipt" + "doctype": "Bin" }, { "type": "report", From fca86c48c8f2062a2b6e03ed5ecc746e58ca308d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 11 Jul 2019 14:39:24 +0530 Subject: [PATCH 59/83] fix: Revert #frappe/erpnext/17896 (#18251) --- erpnext/public/js/utils.js | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index cf48be4128..0cd648ea11 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -573,7 +573,6 @@ erpnext.utils.map_current_doc = function(opts) { if(!r.exc) { var doc = frappe.model.sync(r.message); cur_frm.dirty(); - erpnext.utils.clear_duplicates(); cur_frm.refresh(); } } @@ -604,28 +603,6 @@ erpnext.utils.map_current_doc = function(opts) { } } -erpnext.utils.clear_duplicates = function() { - if(!cur_frm.doc.items) return; - const unique_items = new Map(); - /* - Create a Map of items with - item_code => [qty, warehouse, batch_no] - */ - let items = []; - - for (let item of cur_frm.doc.items) { - if (!(unique_items.has(item.item_code) && unique_items.get(item.item_code)[0] === item.qty && - unique_items.get(item.item_code)[1] === item.warehouse && unique_items.get(item.item_code)[2] === item.batch_no && - unique_items.get(item.item_code)[3] === item.delivery_date && unique_items.get(item.item_code)[4] === item.required_date && - unique_items.get(item.item_code)[5] === item.rate)) { - - unique_items.set(item.item_code, [item.qty, item.warehouse, item.batch_no, item.delivery_date, item.required_date, item.rate]); - items.push(item); - } - } - cur_frm.doc.items = items; -} - frappe.form.link_formatters['Item'] = function(value, doc) { if(doc && doc.item_name && doc.item_name !== value) { return value? value + ': ' + doc.item_name: doc.item_name; From b390b70ee34f273c349a466afeb23f0e358f041c Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 11 Jul 2019 15:22:03 +0530 Subject: [PATCH 60/83] fix: set project in material request when pull items from bom (#18256) --- erpnext/manufacturing/doctype/bom/bom.py | 1 + erpnext/stock/doctype/material_request/material_request.js | 1 + 2 files changed, 2 insertions(+) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 75eb794386..766f6754b6 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -594,6 +594,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite sum(bom_item.{qty_field}/ifnull(bom.quantity, 1)) * %(qty)s as qty, item.description, item.image, + bom.project, item.stock_uom, item.allow_alternative_item, item_default.default_warehouse, diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 5351f32186..96e31ff6ff 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -211,6 +211,7 @@ frappe.ui.form.on('Material Request', { d.stock_uom = item.stock_uom; d.conversion_factor = 1; d.qty = item.qty; + d.project = item.project; }); } d.hide(); From af88e15e92d9bac411e592f20a5339c57c6d7ee9 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 11 Jul 2019 17:14:29 +0530 Subject: [PATCH 61/83] fix(patch): set_priority_for_support.py (#18255) --- .../patches/v12_0/set_priority_for_support.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/erpnext/patches/v12_0/set_priority_for_support.py b/erpnext/patches/v12_0/set_priority_for_support.py index cc290396f8..5096ed4c3c 100644 --- a/erpnext/patches/v12_0/set_priority_for_support.py +++ b/erpnext/patches/v12_0/set_priority_for_support.py @@ -33,19 +33,23 @@ def set_priorities_service_level(): service_level_priorities = frappe.get_list("Service Level", fields=["name", "priority", "response_time", "response_time_period", "resolution_time", "resolution_time_period"]) frappe.reload_doc("support", "doctype", "service_level") + frappe.reload_doc("support", "doctype", "support_settings") + frappe.db.set_value('Support Settings', None, 'track_service_level_agreement', 1) for service_level in service_level_priorities: if service_level: doc = frappe.get_doc("Service Level", service_level.name) - doc.append("priorities", { - "priority": service_level.priority, - "default_priority": 1, - "response_time": service_level.response_time, - "response_time_period": service_level.response_time_period, - "resolution_time": service_level.resolution_time, - "resolution_time_period": service_level.resolution_time_period - }) - doc.save(ignore_permissions=True) + if not doc.priorities: + doc.append("priorities", { + "priority": service_level.priority, + "default_priority": 1, + "response_time": service_level.response_time, + "response_time_period": service_level.response_time_period, + "resolution_time": service_level.resolution_time, + "resolution_time_period": service_level.resolution_time_period + }) + doc.flags.ignore_validate = True + doc.save(ignore_permissions=True) except frappe.db.TableMissingError: frappe.reload_doc("support", "doctype", "service_level") @@ -73,6 +77,7 @@ def set_priorities_service_level_agreement(): "resolution_time": service_level_agreement.resolution_time, "resolution_time_period": service_level_agreement.resolution_time_period }) + doc.flags.ignore_validate = True doc.save(ignore_permissions=True) except frappe.db.TableMissingError: frappe.reload_doc("support", "doctype", "service_level_agreement") \ No newline at end of file From bd055a858c9f14b20fdbb5b269bc68d9e73e4f1d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 11 Jul 2019 17:18:00 +0530 Subject: [PATCH 62/83] fix: UX fixes in loan (#18220) * fix: UX fixes in loan * Update loan.py --- .../employee_loan_application.py | 69 ------------------- erpnext/hr/doctype/loan/loan.js | 32 +++------ erpnext/hr/doctype/loan/loan.json | 44 ++++++++++-- erpnext/hr/doctype/loan/loan.py | 59 ++++++++++------ .../loan_application/loan_application.js | 5 +- .../loan_application/loan_application.py | 12 ++-- 6 files changed, 94 insertions(+), 127 deletions(-) delete mode 100644 erpnext/hr/doctype/employee_loan_application/employee_loan_application.py diff --git a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py b/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py deleted file mode 100644 index b6c650207f..0000000000 --- a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe, math -from frappe import _ -from frappe.utils import flt, rounded -from frappe.model.mapper import get_mapped_doc -from frappe.model.document import Document - -from erpnext.hr.doctype.employee_loan.employee_loan import get_monthly_repayment_amount, check_repayment_method - -class EmployeeLoanApplication(Document): - def validate(self): - check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods) - self.validate_loan_amount() - self.get_repayment_details() - - def validate_loan_amount(self): - maximum_loan_limit = frappe.db.get_value('Loan Type', self.loan_type, 'maximum_loan_amount') - if maximum_loan_limit and self.loan_amount > maximum_loan_limit: - frappe.throw(_("Loan Amount cannot exceed Maximum Loan Amount of {0}").format(maximum_loan_limit)) - - def get_repayment_details(self): - if self.repayment_method == "Repay Over Number of Periods": - self.repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods) - - if self.repayment_method == "Repay Fixed Amount per Period": - monthly_interest_rate = flt(self.rate_of_interest) / (12 *100) - if monthly_interest_rate: - monthly_interest_amount = self.loan_amount * monthly_interest_rate - if monthly_interest_amount >= self.repayment_amount: - frappe.throw(_("Repayment amount {} should be greater than monthly interest amount {}"). - format(self.repayment_amount, monthly_interest_amount)) - - self.repayment_periods = math.ceil((math.log(self.repayment_amount) - - math.log(self.repayment_amount - (monthly_interest_amount))) / - (math.log(1 + monthly_interest_rate))) - else: - self.repayment_periods = self.loan_amount / self.repayment_amount - - self.calculate_payable_amount() - - def calculate_payable_amount(self): - balance_amount = self.loan_amount - self.total_payable_amount = 0 - self.total_payable_interest = 0 - - while(balance_amount > 0): - interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12*100)) - balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount) - - self.total_payable_interest += interest_amount - - self.total_payable_amount = self.loan_amount + self.total_payable_interest - -@frappe.whitelist() -def make_employee_loan(source_name, target_doc = None): - doclist = get_mapped_doc("Employee Loan Application", source_name, { - "Employee Loan Application": { - "doctype": "Employee Loan", - "validation": { - "docstatus": ["=", 1] - } - } - }, target_doc) - - return doclist \ No newline at end of file diff --git a/erpnext/hr/doctype/loan/loan.js b/erpnext/hr/doctype/loan/loan.js index e1b41786f4..3f5c30c475 100644 --- a/erpnext/hr/doctype/loan/loan.js +++ b/erpnext/hr/doctype/loan/loan.js @@ -39,31 +39,19 @@ frappe.ui.form.on('Loan', { }, refresh: function (frm) { - if (frm.doc.docstatus == 1 && frm.doc.status == "Sanctioned") { - frm.add_custom_button(__('Create Disbursement Entry'), function() { - frm.trigger("make_jv"); - }) - } - if (frm.doc.repayment_schedule) { - let total_amount_paid = 0; - $.each(frm.doc.repayment_schedule || [], function(i, row) { - if (row.paid) { - total_amount_paid += row.total_payment; - } - }); - frm.set_value("total_amount_paid", total_amount_paid); -; } - if (frm.doc.docstatus == 1 && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) { - frm.add_custom_button(__('Create Repayment Entry'), function() { - frm.trigger("make_repayment_entry"); - }) + if (frm.doc.docstatus == 1) { + if (frm.doc.status == "Sanctioned") { + frm.add_custom_button(__('Create Disbursement Entry'), function() { + frm.trigger("make_jv"); + }).addClass("btn-primary"); + } else if (frm.doc.status == "Disbursed" && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) { + frm.add_custom_button(__('Create Repayment Entry'), function() { + frm.trigger("make_repayment_entry"); + }).addClass("btn-primary"); + } } frm.trigger("toggle_fields"); }, - status: function (frm) { - frm.toggle_reqd("disbursement_date", frm.doc.status == 'Disbursed') - frm.toggle_reqd("repayment_start_date", frm.doc.status == 'Disbursed') - }, make_jv: function (frm) { frappe.call({ diff --git a/erpnext/hr/doctype/loan/loan.json b/erpnext/hr/doctype/loan/loan.json index 587b3010ca..505b601edd 100644 --- a/erpnext/hr/doctype/loan/loan.json +++ b/erpnext/hr/doctype/loan/loan.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, @@ -20,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "applicant_type", "fieldtype": "Select", "hidden": 0, @@ -53,6 +55,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "applicant", "fieldtype": "Dynamic Link", "hidden": 0, @@ -86,6 +89,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "applicant_name", "fieldtype": "Data", "hidden": 0, @@ -118,6 +122,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "loan_application", "fieldtype": "Link", "hidden": 0, @@ -151,6 +156,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "loan_type", "fieldtype": "Link", "hidden": 0, @@ -184,6 +190,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_3", "fieldtype": "Column Break", "hidden": 0, @@ -215,7 +222,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "default": "", + "default": "Today", + "fetch_if_empty": 0, "fieldname": "posting_date", "fieldtype": "Date", "hidden": 0, @@ -248,6 +256,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "company", "fieldtype": "Link", "hidden": 0, @@ -282,6 +291,7 @@ "collapsible": 0, "columns": 0, "default": "Sanctioned", + "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", "hidden": 0, @@ -299,7 +309,7 @@ "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, @@ -316,6 +326,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.applicant_type==\"Employee\"", + "fetch_if_empty": 0, "fieldname": "repay_from_salary", "fieldtype": "Check", "hidden": 0, @@ -348,6 +359,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_8", "fieldtype": "Section Break", "hidden": 0, @@ -380,6 +392,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "loan_amount", "fieldtype": "Currency", "hidden": 0, @@ -415,6 +428,7 @@ "columns": 0, "default": "", "fetch_from": "loan_type.rate_of_interest", + "fetch_if_empty": 0, "fieldname": "rate_of_interest", "fieldtype": "Percent", "hidden": 0, @@ -448,6 +462,8 @@ "bold": 0, "collapsible": 0, "columns": 0, + "depends_on": "eval:doc.status==\"Disbursed\"", + "fetch_if_empty": 0, "fieldname": "disbursement_date", "fieldtype": "Date", "hidden": 0, @@ -480,6 +496,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "repayment_start_date", "fieldtype": "Date", "hidden": 0, @@ -499,7 +516,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -512,6 +529,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_11", "fieldtype": "Column Break", "hidden": 0, @@ -544,6 +562,7 @@ "collapsible": 0, "columns": 0, "default": "Repay Over Number of Periods", + "fetch_if_empty": 0, "fieldname": "repayment_method", "fieldtype": "Select", "hidden": 0, @@ -579,6 +598,7 @@ "columns": 0, "default": "", "depends_on": "", + "fetch_if_empty": 0, "fieldname": "repayment_periods", "fieldtype": "Int", "hidden": 0, @@ -613,6 +633,7 @@ "columns": 0, "default": "", "depends_on": "", + "fetch_if_empty": 0, "fieldname": "monthly_repayment_amount", "fieldtype": "Currency", "hidden": 0, @@ -646,6 +667,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "account_info", "fieldtype": "Section Break", "hidden": 0, @@ -678,6 +700,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "mode_of_payment", "fieldtype": "Link", "hidden": 0, @@ -711,6 +734,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "payment_account", "fieldtype": "Link", "hidden": 0, @@ -744,6 +768,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_9", "fieldtype": "Column Break", "hidden": 0, @@ -775,6 +800,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "loan_account", "fieldtype": "Link", "hidden": 0, @@ -808,6 +834,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "interest_income_account", "fieldtype": "Link", "hidden": 0, @@ -841,6 +868,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_15", "fieldtype": "Section Break", "hidden": 0, @@ -873,6 +901,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "repayment_schedule", "fieldtype": "Table", "hidden": 0, @@ -906,6 +935,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_17", "fieldtype": "Section Break", "hidden": 0, @@ -939,6 +969,7 @@ "collapsible": 0, "columns": 0, "default": "0", + "fetch_if_empty": 0, "fieldname": "total_payment", "fieldtype": "Currency", "hidden": 0, @@ -972,6 +1003,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_19", "fieldtype": "Column Break", "hidden": 0, @@ -1004,6 +1036,7 @@ "collapsible": 0, "columns": 0, "default": "0", + "fetch_if_empty": 0, "fieldname": "total_interest_payable", "fieldtype": "Currency", "hidden": 0, @@ -1037,6 +1070,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_amount_paid", "fieldtype": "Currency", "hidden": 0, @@ -1070,6 +1104,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", "hidden": 0, @@ -1106,7 +1141,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-21 16:15:53.267145", + "modified": "2019-07-10 13:04:20.953694", "modified_by": "Administrator", "module": "HR", "name": "Loan", @@ -1149,7 +1184,6 @@ "set_user_permissions": 0, "share": 0, "submit": 0, - "user_permission_doctypes": "[\"Employee\"]", "write": 0 } ], diff --git a/erpnext/hr/doctype/loan/loan.py b/erpnext/hr/doctype/loan/loan.py index 58c9b8f667..a803863124 100644 --- a/erpnext/hr/doctype/loan/loan.py +++ b/erpnext/hr/doctype/loan/loan.py @@ -6,29 +6,33 @@ from __future__ import unicode_literals import frappe, math, json import erpnext from frappe import _ -from frappe.utils import flt, rounded, add_months, nowdate +from frappe.utils import flt, rounded, add_months, nowdate, getdate from erpnext.controllers.accounts_controller import AccountsController class Loan(AccountsController): def validate(self): - check_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods) + validate_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods) + self.set_missing_fields() + self.make_repayment_schedule() + self.set_repayment_period() + self.calculate_totals() + + def set_missing_fields(self): if not self.company: self.company = erpnext.get_default_company() + if not self.posting_date: self.posting_date = nowdate() + if self.loan_type and not self.rate_of_interest: self.rate_of_interest = frappe.db.get_value("Loan Type", self.loan_type, "rate_of_interest") + if self.repayment_method == "Repay Over Number of Periods": self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods) + if self.status == "Repaid/Closed": self.total_amount_paid = self.total_payment - if self.status == 'Disbursed' and self.repayment_start_date < self.disbursement_date: - frappe.throw(_("Repayment Start Date cannot be before Disbursement Date.")) - if self.status == "Disbursed": - self.make_repayment_schedule() - self.set_repayment_period() - self.calculate_totals() def make_jv_entry(self): self.check_permission('write') @@ -105,20 +109,31 @@ def update_total_amount_paid(doc): frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid) def update_disbursement_status(doc): - disbursement = frappe.db.sql("""select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount - from `tabGL Entry` where account = %s and against_voucher_type = 'Loan' and against_voucher = %s""", - (doc.payment_account, doc.name), as_dict=1)[0] - if disbursement.disbursed_amount == doc.loan_amount: - frappe.db.set_value("Loan", doc.name , "status", "Disbursed") - if disbursement.disbursed_amount == 0: - frappe.db.set_value("Loan", doc.name , "status", "Sanctioned") - if disbursement.disbursed_amount > doc.loan_amount: - frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount)) - if disbursement.disbursed_amount > 0: - frappe.db.set_value("Loan", doc.name , "disbursement_date", disbursement.posting_date) - frappe.db.set_value("Loan", doc.name , "repayment_start_date", disbursement.posting_date) + disbursement = frappe.db.sql(""" + select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount + from `tabGL Entry` + where account = %s and against_voucher_type = 'Loan' and against_voucher = %s + """, (doc.payment_account, doc.name), as_dict=1)[0] -def check_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods): + disbursement_date = None + if not disbursement or disbursement.disbursed_amount == 0: + status = "Sanctioned" + elif disbursement.disbursed_amount == doc.loan_amount: + disbursement_date = disbursement.posting_date + status = "Disbursed" + elif disbursement.disbursed_amount > doc.loan_amount: + frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount)) + + if status == 'Disbursed' and getdate(disbursement_date) > getdate(frappe.db.get_value("Loan", doc.name, "repayment_start_date")): + frappe.throw(_("Disbursement Date cannot be after Loan Repayment Start Date")) + + frappe.db.sql(""" + update `tabLoan` + set status = %s, disbursement_date = %s + where name = %s + """, (status, disbursement_date, doc.name)) + +def validate_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods): if repayment_method == "Repay Over Number of Periods" and not repayment_periods: frappe.throw(_("Please enter Repayment Periods")) @@ -222,4 +237,4 @@ def make_jv_entry(loan, company, loan_account, applicant_type, applicant, loan_a "reference_name": loan, }) journal_entry.set("accounts", account_amt_list) - return journal_entry.as_dict() \ No newline at end of file + return journal_entry.as_dict() diff --git a/erpnext/hr/doctype/loan_application/loan_application.js b/erpnext/hr/doctype/loan_application/loan_application.js index febcbd88e7..a73b62a894 100644 --- a/erpnext/hr/doctype/loan_application/loan_application.js +++ b/erpnext/hr/doctype/loan_application/loan_application.js @@ -23,9 +23,8 @@ frappe.ui.form.on('Loan Application', { }, add_toolbar_buttons: function(frm) { if (frm.doc.status == "Approved") { - frm.add_custom_button(__('Loan'), function() { + frm.add_custom_button(__('Create Loan'), function() { frappe.call({ - type: "GET", method: "erpnext.hr.doctype.loan_application.loan_application.make_loan", args: { "source_name": frm.doc.name @@ -37,7 +36,7 @@ frappe.ui.form.on('Loan Application', { } } }); - }) + }).addClass("btn-primary"); } } }); diff --git a/erpnext/hr/doctype/loan_application/loan_application.py b/erpnext/hr/doctype/loan_application/loan_application.py index 706c9646c7..5dbcf15eac 100644 --- a/erpnext/hr/doctype/loan_application/loan_application.py +++ b/erpnext/hr/doctype/loan_application/loan_application.py @@ -9,11 +9,11 @@ from frappe.utils import flt, rounded from frappe.model.mapper import get_mapped_doc from frappe.model.document import Document -from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, check_repayment_method +from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, validate_repayment_method class LoanApplication(Document): def validate(self): - check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods) + validate_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods) self.validate_loan_amount() self.get_repayment_details() @@ -29,14 +29,14 @@ class LoanApplication(Document): if self.repayment_method == "Repay Fixed Amount per Period": monthly_interest_rate = flt(self.rate_of_interest) / (12 *100) if monthly_interest_rate: - self.repayment_periods = math.ceil((math.log(self.repayment_amount) - + self.repayment_periods = math.ceil((math.log(self.repayment_amount) - math.log(self.repayment_amount - (self.loan_amount*monthly_interest_rate))) / (math.log(1 + monthly_interest_rate))) else: self.repayment_periods = self.loan_amount / self.repayment_amount self.calculate_payable_amount() - + def calculate_payable_amount(self): balance_amount = self.loan_amount self.total_payable_amount = 0 @@ -47,9 +47,9 @@ class LoanApplication(Document): balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount) self.total_payable_interest += interest_amount - + self.total_payable_amount = self.loan_amount + self.total_payable_interest - + @frappe.whitelist() def make_loan(source_name, target_doc = None): doclist = get_mapped_doc("Loan Application", source_name, { From 6bbb726769bba3cca3f211fdba3e1b2c0d5a0c09 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 11 Jul 2019 18:04:41 +0530 Subject: [PATCH 63/83] fix: Remove unused imports --- .../doctype/quickbooks_migrator/quickbooks_migrator.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py index 29a1a2b0bb..d201a9e7b8 100644 --- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py +++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py @@ -7,7 +7,7 @@ import frappe from frappe import _ from frappe.model.document import Document from requests_oauthlib import OAuth2Session -import json, requests +import json, requests, traceback from erpnext import encode_company_abbr # QuickBooks requires a redirect URL, User will be redirect to this URL @@ -32,7 +32,6 @@ def callback(*args, **kwargs): class QuickBooksMigrator(Document): def __init__(self, *args, **kwargs): super(QuickBooksMigrator, self).__init__(*args, **kwargs) - from pprint import pprint self.oauth = OAuth2Session( client_id=self.client_id, redirect_uri=self.redirect_url, @@ -1252,8 +1251,6 @@ class QuickBooksMigrator(Document): def _log_error(self, execption, data=""): - import json, traceback - traceback.print_exc() frappe.log_error(title="QuickBooks Migration Error", message="\n".join([ "Data", From dfeb6bef8c778e7ee75764063469f0a32f5197fd Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 11 Jul 2019 18:08:46 +0530 Subject: [PATCH 64/83] style: Remove whitespace before : --- .../quickbooks_migrator/quickbooks_migrator.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py index d201a9e7b8..9f3e50c26d 100644 --- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py +++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py @@ -497,12 +497,12 @@ class QuickBooksMigrator(Document): erpcustomer = frappe.get_doc({ "doctype": "Customer", "quickbooks_id": customer["Id"], - "customer_name" : encode_company_abbr(customer["DisplayName"], self.company), - "customer_type" : "Individual", - "customer_group" : "Commercial", + "customer_name": encode_company_abbr(customer["DisplayName"], self.company), + "customer_type": "Individual", + "customer_group": "Commercial", "default_currency": customer["CurrencyRef"]["value"], "accounts": [{"company": self.company, "account": receivable_account}], - "territory" : "All Territories", + "territory": "All Territories", "company": self.company, }).insert() if "BillAddr" in customer: @@ -520,7 +520,7 @@ class QuickBooksMigrator(Document): item_dict = { "doctype": "Item", "quickbooks_id": item["Id"], - "item_code" : encode_company_abbr(item["Name"], self.company), + "item_code": encode_company_abbr(item["Name"], self.company), "stock_uom": "Unit", "is_stock_item": 0, "item_group": "All Item Groups", @@ -548,8 +548,8 @@ class QuickBooksMigrator(Document): erpsupplier = frappe.get_doc({ "doctype": "Supplier", "quickbooks_id": vendor["Id"], - "supplier_name" : encode_company_abbr(vendor["DisplayName"], self.company), - "supplier_group" : "All Supplier Groups", + "supplier_name": encode_company_abbr(vendor["DisplayName"], self.company), + "supplier_group": "All Supplier Groups", "company": self.company, }).insert() if "BillAddr" in vendor: @@ -1199,7 +1199,7 @@ class QuickBooksMigrator(Document): def _create_address(self, entity, doctype, address, address_type): - try : + try: if not frappe.db.exists({"doctype": "Address", "quickbooks_id": address["Id"]}): frappe.get_doc({ "doctype": "Address", From 9f40ffcd1e066b23d738a2e2781e5673ce302c0e Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 11 Jul 2019 18:10:05 +0530 Subject: [PATCH 65/83] style: Insert whitespace after , --- .../doctype/quickbooks_migrator/quickbooks_migrator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py index 9f3e50c26d..b33621f406 100644 --- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py +++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py @@ -217,7 +217,7 @@ class QuickBooksMigrator(Document): def _fetch_general_ledger(self): try: - query_uri = "{}/company/{}/reports/GeneralLedger".format(self.api_endpoint ,self.quickbooks_company_id) + query_uri = "{}/company/{}/reports/GeneralLedger".format(self.api_endpoint, self.quickbooks_company_id) response = self._get(query_uri, params={ "columns": ",".join(["tx_date", "txn_type", "credit_amt", "debt_amt"]), @@ -555,7 +555,7 @@ class QuickBooksMigrator(Document): if "BillAddr" in vendor: self._create_address(erpsupplier, "Supplier", vendor["BillAddr"], "Billing") if "ShipAddr" in vendor: - self._create_address(erpsupplier, "Supplier",vendor["ShipAddr"], "Shipping") + self._create_address(erpsupplier, "Supplier", vendor["ShipAddr"], "Shipping") except Exception as e: self._log_error(e) From 2f6cd1999a0b886a2ddd85df9621f2d5601ebfab Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 11 Jul 2019 18:16:23 +0530 Subject: [PATCH 66/83] style: One import per line --- .../doctype/quickbooks_migrator/quickbooks_migrator.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py index b33621f406..16cf2e1db8 100644 --- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py +++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py @@ -7,7 +7,9 @@ import frappe from frappe import _ from frappe.model.document import Document from requests_oauthlib import OAuth2Session -import json, requests, traceback +import json +import requests +import traceback from erpnext import encode_company_abbr # QuickBooks requires a redirect URL, User will be redirect to this URL From 7352227a24de7d326e538f1b406f91e7e11645b1 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 11 Jul 2019 18:16:42 +0530 Subject: [PATCH 67/83] fix: Remove unused local variable --- .../doctype/quickbooks_migrator/quickbooks_migrator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py index 16cf2e1db8..86c26fbab4 100644 --- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py +++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py @@ -494,7 +494,7 @@ class QuickBooksMigrator(Document): "account_currency": customer["CurrencyRef"]["value"], "company": self.company, })[0]["name"] - except Exception as e: + except Exception: receivable_account = None erpcustomer = frappe.get_doc({ "doctype": "Customer", From d4c10801d12812d2611c5ac121086ff1d57af845 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 11 Jul 2019 18:17:01 +0530 Subject: [PATCH 68/83] style: Remove multiple whitespace after : --- .../doctype/quickbooks_migrator/quickbooks_migrator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py index 86c26fbab4..85fe4f2303 100644 --- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py +++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py @@ -830,7 +830,7 @@ class QuickBooksMigrator(Document): "currency": invoice["CurrencyRef"]["value"], "conversion_rate": invoice.get("ExchangeRate", 1), "posting_date": invoice["TxnDate"], - "due_date": invoice.get("DueDate", invoice["TxnDate"]), + "due_date": invoice.get("DueDate", invoice["TxnDate"]), "credit_to": credit_to_account, "supplier": frappe.get_all("Supplier", filters={ From f8c92f30bf1a1db415dd76004d75db7d404088eb Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 11 Jul 2019 18:27:19 +0530 Subject: [PATCH 69/83] feat(purchase-order): Add tax category field in purchase order --- .../doctype/purchase_order/purchase_order.json | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index dead1f030c..5dce349782 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -66,10 +66,11 @@ "net_total", "total_net_weight", "taxes_section", - "taxes_and_charges", + "tax_category", "column_break_50", "shipping_rule", "section_break_52", + "taxes_and_charges", "taxes", "sec_tax_breakup", "other_charges_calculation", @@ -569,7 +570,7 @@ { "fieldname": "taxes_and_charges", "fieldtype": "Link", - "label": "Taxes and Charges", + "label": "Purchase Taxes and Charges Template", "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", @@ -1032,12 +1033,18 @@ "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", "label": "Update Auto Repeat Reference" + }, + { + "fieldname": "tax_category", + "fieldtype": "Link", + "label": "Tax Category", + "options": "Tax Category" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, - "modified": "2019-06-24 21:22:05.483429", + "modified": "2019-07-11 18:25:49.509343", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", From f04b19aa2e8c50920b26b412fc61d8b579879fdb Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 11 Jul 2019 18:29:24 +0530 Subject: [PATCH 70/83] fix: Company might not have any warehouses when on_update hook for QBM is being executed This used to throw while renaming a Company that is either used with QuickBooks Migrator or is set as a Default Company in Global Defaults. --- .../doctype/quickbooks_migrator/quickbooks_migrator.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py index 85fe4f2303..96a533ee10 100644 --- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py +++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py @@ -47,7 +47,9 @@ class QuickBooksMigrator(Document): if self.company: # We need a Cost Center corresponding to the selected erpnext Company self.default_cost_center = frappe.db.get_value('Company', self.company, 'cost_center') - self.default_warehouse = frappe.get_all('Warehouse', filters={"company": self.company, "is_group": 0})[0]["name"] + company_warehouses = frappe.get_all('Warehouse', filters={"company": self.company, "is_group": 0}) + if company_warehouses: + self.default_warehouse = company_warehouses[0].name if self.authorization_endpoint: self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0] From 53e7df078ec5fe5265c5ae28ae01a59e2151157c Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Fri, 12 Jul 2019 12:05:39 +0530 Subject: [PATCH 71/83] fix: set project link in item tables (#18233) --- .../doctype/material_request/material_request.py | 2 +- .../stock_entry_detail/stock_entry_detail.json | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 2b079e7995..f2fe44879d 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -450,7 +450,7 @@ def make_stock_entry(source_name, target_doc=None): "field_map": { "name": "material_request_item", "parent": "material_request", - "uom": "stock_uom", + "uom": "stock_uom" }, "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.stock_qty diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 5933700457..912a4651b3 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -61,7 +61,8 @@ "ste_detail", "column_break_51", "transferred_qty", - "reference_purchase_receipt" + "reference_purchase_receipt", + "project" ], "fields": [ { @@ -472,11 +473,18 @@ "label": "Reference Purchase Receipt", "options": "Purchase Receipt", "read_only": 1 + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project", + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2019-06-14 11:58:41.958144", + "modified": "2019-07-12 11:34:53.190749", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", From 7d1d9aa46bbcb9e50774cb4f90adac12f6536683 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Fri, 12 Jul 2019 06:49:35 +0000 Subject: [PATCH 72/83] feat(Issue): Reset SLA (#18244) * feat: reset sla * fix: db.get_single_value * feat: timeline entry for reset sla * fix: remove communication from support config * fix: reset sla on split_issue --- erpnext/config/support.py | 8 +--- erpnext/support/doctype/issue/issue.js | 42 +++++++++++++++++++ erpnext/support/doctype/issue/issue.json | 23 ++++++++-- erpnext/support/doctype/issue/issue.py | 21 +++++++++- erpnext/support/doctype/issue/test_issue.py | 3 +- .../test_service_level_agreement.py | 1 + .../support_settings/support_settings.json | 9 +++- 7 files changed, 93 insertions(+), 14 deletions(-) diff --git a/erpnext/config/support.py b/erpnext/config/support.py index 36b4214196..151c4f743e 100644 --- a/erpnext/config/support.py +++ b/erpnext/config/support.py @@ -21,13 +21,7 @@ def get_data(): "type": "doctype", "name": "Issue Priority", "description": _("Issue Priority."), - }, - { - "type": "doctype", - "name": "Communication", - "description": _("Communication log."), - "onboard": 1, - }, + } ] }, { diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 2d9650c5a4..aec9db9d4a 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -2,6 +2,12 @@ frappe.ui.form.on("Issue", { onload: function(frm) { frm.email_field = "raised_by"; + frappe.db.get_value("Support Settings", {name: "Support Settings"}, "allow_resetting_service_level_agreement", (r) => { + if (!r.allow_resetting_service_level_agreement) { + frm.set_df_property("reset_service_level_agreement", "hidden", 1) ; + } + }); + if (frm.doc.service_level_agreement) { frappe.call({ method: "erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters", @@ -73,6 +79,42 @@ frappe.ui.form.on("Issue", { } }, + reset_service_level_agreement: function(frm) { + let reset_sla = new frappe.ui.Dialog({ + title: __("Reset Service Level Agreement"), + fields: [ + { + fieldtype: "Data", + fieldname: "reason", + label: __("Reason"), + reqd: 1 + } + ], + primary_action_label: __("Reset"), + primary_action: (values) => { + reset_sla.disable_primary_action(); + reset_sla.hide(); + reset_sla.clear(); + + frappe.show_alert({ + indicator: 'green', + message: __('Resetting Service Level Agreement.') + }); + + frm.call("reset_service_level_agreement", { + reason: values.reason, + user: frappe.session.user_email + }, () => { + reset_sla.enable_primary_action(); + frm.refresh(); + frappe.msgprint(__("Service Level Agreement Reset.")); + }); + } + }); + + reset_sla.show(); + }, + timeline_refresh: function(frm) { // create button for "Help Article" if(frappe.model.can_create('Help Article')) { diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 72153dcdea..41fe380e75 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -22,10 +22,12 @@ "service_level_agreement", "response_by", "response_by_variance", + "reset_service_level_agreement", "cb", "agreement_fulfilled", "resolution_by", "resolution_by_variance", + "service_level_agreement_creation", "response", "mins_to_first_response", "first_responded_on", @@ -68,9 +70,9 @@ "fieldname": "subject", "fieldtype": "Data", "in_global_search": 1, + "in_standard_filter": 1, "label": "Subject", - "reqd": 1, - "in_standard_filter": 1 + "reqd": 1 }, { "fieldname": "customer", @@ -336,11 +338,24 @@ "fieldtype": "Float", "label": "Resolution By Variance", "read_only": 1 + }, + { + "fieldname": "service_level_agreement_creation", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Service Level Agreement Creation", + "read_only": 1 + }, + { + "depends_on": "eval: doc.service_level_agreement", + "fieldname": "reset_service_level_agreement", + "fieldtype": "Button", + "label": "Reset Service Level Agreement" } ], "icon": "fa fa-ticket", "idx": 7, - "modified": "2019-06-30 13:19:38.215525", + "modified": "2019-07-11 23:57:22.015881", "modified_by": "Administrator", "module": "Support", "name": "Issue", @@ -365,4 +380,4 @@ "timeline_field": "customer", "title_field": "subject", "track_seen": 1 -} +} \ No newline at end of file diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 226676fec3..ce9fb12145 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -121,6 +121,7 @@ class Issue(Document): # Reset SLA if replicated_issue.service_level_agreement: + replicated_issue.service_level_agreement_creation = now_datetime() replicated_issue.service_level_agreement = None replicated_issue.agreement_fulfilled = "Ongoing" replicated_issue.response_by = None @@ -173,8 +174,9 @@ class Issue(Document): if not self.creation: self.creation = now_datetime() + self.service_level_agreement_creation = now_datetime() - start_date_time = get_datetime(self.creation) + start_date_time = get_datetime(self.service_level_agreement_creation) self.response_by = get_expected_time_for(parameter='response', service_level=priority, start_date_time=start_date_time) self.resolution_by = get_expected_time_for(parameter='resolution', service_level=priority, start_date_time=start_date_time) @@ -193,6 +195,23 @@ class Issue(Document): self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) frappe.msgprint(_("Service Level Agreement has been changed to {0}.").format(self.service_level_agreement)) + def reset_service_level_agreement(self, reason, user): + if not frappe.db.get_single_value("Support Settings", "allow_resetting_service_level_agreement"): + frappe.throw(_("Allow Resetting Service Level Agreement from Support Settings.")) + + frappe.get_doc({ + "doctype": "Comment", + "comment_type": "Info", + "reference_doctype": self.doctype, + "reference_name": self.name, + "comment_email": user, + "content": " resetted Service Level Agreement - {0}".format(_(reason)), + }).insert(ignore_permissions=True) + + self.service_level_agreement_creation = now_datetime() + self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) + self.save() + def get_expected_time_for(parameter, service_level, start_date_time): current_date_time = start_date_time expected_time = current_date_time diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index eb1736e6c9..7a5e3e300d 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -80,7 +80,8 @@ def make_issue(creation=None, customer=None, index=0): "customer": customer, "raised_by": "test@example.com", "description": "Service Level Agreement Issue", - "creation": creation + "creation": creation, + "service_level_agreement_creation": creation }).insert(ignore_permissions=True) return issue diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 68b82d10ec..4a741ea4e1 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -76,6 +76,7 @@ def create_service_level_agreement(default_service_level_agreement, service_leve service_level_agreement = frappe.get_doc({ "doctype": "Service Level Agreement", + "enable": 1, "default_service_level_agreement": default_service_level_agreement, "service_level": service_level, "holiday_list": holiday_list, diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json index 2dced15d4e..be9e064591 100644 --- a/erpnext/support/doctype/support_settings/support_settings.json +++ b/erpnext/support/doctype/support_settings/support_settings.json @@ -6,6 +6,7 @@ "field_order": [ "sb_00", "track_service_level_agreement", + "allow_resetting_service_level_agreement", "issues_sb", "close_issue_after_days", "portal_sb", @@ -118,10 +119,16 @@ "fieldname": "track_service_level_agreement", "fieldtype": "Check", "label": "Track Service Level Agreement" + }, + { + "default": "0", + "fieldname": "allow_resetting_service_level_agreement", + "fieldtype": "Check", + "label": "Allow Resetting Service Level Agreement" } ], "issingle": 1, - "modified": "2019-07-09 17:11:38.216732", + "modified": "2019-07-10 22:52:39.663873", "modified_by": "Administrator", "module": "Support", "name": "Support Settings", From 91417646b105e453d69480e062d2146c685487d2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 12 Jul 2019 12:24:05 +0530 Subject: [PATCH 73/83] Fix: Add fieldtype as currency --- erpnext/accounts/report/cash_flow/cash_flow.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index 626d18514e..cd3d8dc125 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -187,5 +187,7 @@ def get_chart_data(columns, data): }, "type": "bar" } + + chart["fieldtype"] = "Currency" - return chart \ No newline at end of file + return chart From 5073ac4dcb7bfd46fab64ce1ad261adcbe96ecc2 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Fri, 12 Jul 2019 12:28:34 +0530 Subject: [PATCH 74/83] fix: Patient History (Patient Medical Record) (#17972) * Page - Patient History * Healthcare Utils - get patient vitals * Healthcare Utils - render doc as html for patient history page * Page - Patient History - prcatitioner image in time line * Page - Patient History in menu * fix - page medical history * fix: page patient_history broken img, minor fixes * fix: patient_history page, add pagination, show doc if fetched * fix: Patient Medical Record to Patient History * fix: patient history page - set route options * fix: update to code standards * fix: remove Eidt button form patient history * fix: css update to code standards * fix: Remove page medical_record --- erpnext/config/healthcare.py | 4 +- erpnext/healthcare/doctype/patient/patient.js | 4 +- .../patient_appointment.js | 4 +- .../patient_encounter/patient_encounter.js | 4 +- .../page/medical_record/__init__.py | 1 - .../page/medical_record/medical_record.js | 182 ----------- .../page/medical_record/medical_record.py | 24 -- .../medical_record/medical_record_row.html | 21 -- .../page/medical_record/patient_select.html | 5 - .../page/patient_history/__init__.py | 0 .../patient_history.css} | 64 +++- .../page/patient_history/patient_history.html | 20 ++ .../page/patient_history/patient_history.js | 300 ++++++++++++++++++ .../patient_history.json} | 15 +- .../page/patient_history/patient_history.py | 39 +++ erpnext/healthcare/utils.py | 113 +++++++ erpnext/patches.txt | 1 + .../remove_patient_medical_record_page.py | 7 + 18 files changed, 556 insertions(+), 252 deletions(-) delete mode 100644 erpnext/healthcare/page/medical_record/__init__.py delete mode 100644 erpnext/healthcare/page/medical_record/medical_record.js delete mode 100644 erpnext/healthcare/page/medical_record/medical_record.py delete mode 100644 erpnext/healthcare/page/medical_record/medical_record_row.html delete mode 100644 erpnext/healthcare/page/medical_record/patient_select.html create mode 100644 erpnext/healthcare/page/patient_history/__init__.py rename erpnext/healthcare/page/{medical_record/medical_record.css => patient_history/patient_history.css} (62%) create mode 100644 erpnext/healthcare/page/patient_history/patient_history.html create mode 100644 erpnext/healthcare/page/patient_history/patient_history.js rename erpnext/healthcare/page/{medical_record/medical_record.json => patient_history/patient_history.json} (57%) create mode 100644 erpnext/healthcare/page/patient_history/patient_history.py create mode 100644 erpnext/patches/v12_0/remove_patient_medical_record_page.py diff --git a/erpnext/config/healthcare.py b/erpnext/config/healthcare.py index 1311111618..756d22e416 100644 --- a/erpnext/config/healthcare.py +++ b/erpnext/config/healthcare.py @@ -26,8 +26,8 @@ def get_data(): }, { "type": "page", - "name": "medical_record", - "label": _("Patient Medical Record"), + "name": "patient_history", + "label": _("Patient History"), }, { "type": "page", diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js index 169281430c..1a34fe8076 100644 --- a/erpnext/healthcare/doctype/patient/patient.js +++ b/erpnext/healthcare/doctype/patient/patient.js @@ -21,9 +21,9 @@ frappe.ui.form.on('Patient', { }); } if (frm.doc.patient_name && frappe.user.has_role("Physician")) { - frm.add_custom_button(__('Medical Record'), function () { + frm.add_custom_button(__('Patient History'), function () { frappe.route_options = { "patient": frm.doc.name }; - frappe.set_route("medical_record"); + frappe.set_route("patient_history"); },"View"); } if (!frm.doc.__islocal && (frappe.user.has_role("Nursing User") || frappe.user.has_role("Physician"))) { diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index b3cbd1f753..858145eef3 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -30,9 +30,9 @@ frappe.ui.form.on('Patient Appointment', { }; }); if(frm.doc.patient){ - frm.add_custom_button(__('Medical Record'), function() { + frm.add_custom_button(__('Patient History'), function() { frappe.route_options = {"patient": frm.doc.patient}; - frappe.set_route("medical_record"); + frappe.set_route("patient_history"); },__("View")); } if(frm.doc.status == "Open"){ diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 7ea45688fd..088bc8161b 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -41,10 +41,10 @@ frappe.ui.form.on('Patient Encounter', { } }); } - frm.add_custom_button(__('Medical Record'), function() { + frm.add_custom_button(__('Patient History'), function() { if (frm.doc.patient) { frappe.route_options = {"patient": frm.doc.patient}; - frappe.set_route("medical_record"); + frappe.set_route("patient_history"); } else { frappe.msgprint(__("Please select Patient")); } diff --git a/erpnext/healthcare/page/medical_record/__init__.py b/erpnext/healthcare/page/medical_record/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/healthcare/page/medical_record/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/healthcare/page/medical_record/medical_record.js b/erpnext/healthcare/page/medical_record/medical_record.js deleted file mode 100644 index df19d8f4dc..0000000000 --- a/erpnext/healthcare/page/medical_record/medical_record.js +++ /dev/null @@ -1,182 +0,0 @@ -frappe.provide("frappe.medical_record"); -frappe.pages['medical_record'].on_page_load = function(wrapper) { - var me = this; - var page = frappe.ui.make_app_page({ - parent: wrapper, - title: 'Medical Record', - }); - - frappe.breadcrumbs.add("Medical"); - - page.main.html(frappe.render_template("patient_select", {})); - var patient = frappe.ui.form.make_control({ - parent: page.main.find(".patient"), - df: { - fieldtype: "Link", - options: "Patient", - fieldname: "patient", - change: function(){ - page.main.find(".frappe-list").html(""); - draw_page(patient.get_value(), me); - } - }, - only_input: true, - }); - patient.refresh(); - - - this.page.main.on("click", ".medical_record-message", function() { - var doctype = $(this).attr("data-doctype"), - docname = $(this).attr("data-docname"); - - if (doctype && docname) { - frappe.route_options = { - scroll_to: { "doctype": doctype, "name": docname } - }; - frappe.set_route(["Form", doctype, docname]); - } - }); - - this.page.sidebar.on("click", ".edit-details", function() { - patient = patient.get_value(); - if (patient) { - frappe.set_route(["Form", "Patient", patient]); - } - }); - -}; - -frappe.pages['medical_record'].refresh = function() { - var me = this; - - if(frappe.route_options) { - if(frappe.route_options.patient){ - me.page.main.find(".frappe-list").html(""); - var patient = frappe.route_options.patient; - draw_page(patient,me); - me.page.main.find("[data-fieldname='patient']").val(patient); - frappe.route_options = null; - } - } -}; -var show_patient_info = function(patient, me){ - frappe.call({ - "method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail", - args: { - patient: patient - }, - callback: function (r) { - var data = r.message; - var details = ""; - if(data.email) details += "
Email : " + data.email; - if(data.mobile) details += "
Mobile : " + data.mobile; - if(data.occupation) details += "
Occupation : " + data.occupation; - if(data.blood_group) details += "
Blood group : " + data.blood_group; - if(data.allergies) details += "

Allergies : "+ data.allergies; - if(data.medication) details += "
Medication : "+ data.medication; - if(data.alcohol_current_use) details += "

Alcohol use : "+ data.alcohol_current_use; - if(data.alcohol_past_use) details += "
Alcohol past use : "+ data.alcohol_past_use; - if(data.tobacco_current_use) details += "
Tobacco use : "+ data.tobacco_current_use; - if(data.tobacco_past_use) details += "
Tobacco past use : "+ data.tobacco_past_use; - if(data.medical_history) details += "

Medical history : "+ data.medical_history; - if(data.surgical_history) details += "
Surgical history : "+ data.surgical_history; - if(data.surrounding_factors) details += "

Occupational hazards : "+ data.surrounding_factors; - if(data.other_risk_factors) details += "
Other risk factors : " + data.other_risk_factors; - if(data.patient_details) details += "

More info : " + data.patient_details; - - if(details){ - details = "

Patient Details" + details + "
"; - } - - var vitals = ""; - if(data.temperature) vitals += "
Temperature : " + data.temperature; - if(data.pulse) vitals += "
Pulse : " + data.pulse; - if(data.respiratory_rate) vitals += "
Respiratory Rate : " + data.respiratory_rate; - if(data.bp) vitals += "
BP : " + data.bp; - if(data.bmi) vitals += "
BMI : " + data.bmi; - if(data.height) vitals += "
Height : " + data.height; - if(data.weight) vitals += "
Weight : " + data.weight; - if(data.signs_date) vitals += "
Date : " + data.signs_date; - - if(vitals){ - vitals = "

Vital Signs" + vitals + "
"; - details = vitals + details; - } - if(details) details += ""; - - me.page.sidebar.addClass("col-sm-3"); - me.page.sidebar.html(details); - me.page.wrapper.find(".layout-main-section-wrapper").addClass("col-sm-9"); - } - }); -}; -var draw_page = function(patient, me){ - frappe.model.with_doctype("Patient Medical Record", function() { - me.page.list = new frappe.ui.BaseList({ - hide_refresh: true, - page: me.page, - method: 'erpnext.healthcare.page.medical_record.medical_record.get_feed', - args: {name: patient}, - parent: $("
").appendTo(me.page.main), - render_view: function(values) { - var me = this; - var wrapper = me.page.main.find(".result-list").get(0); - values.map(function (value) { - var row = $('
').data("data", value).appendTo($(wrapper)).get(0); - new frappe.medical_record.Feed(row, value); - }); - }, - show_filters: true, - doctype: "Patient Medical Record", - }); - show_patient_info(patient, me); - me.page.list.run(); - }); -}; - -frappe.medical_record.last_feed_date = false; -frappe.medical_record.Feed = Class.extend({ - init: function(row, data) { - this.scrub_data(data); - this.add_date_separator(row, data); - if(!data.add_class) - data.add_class = "label-default"; - - data.link = ""; - if (data.reference_doctype && data.reference_name) { - data.link = frappe.format(data.reference_name, {fieldtype: "Link", options: data.reference_doctype}, - {label: __(data.reference_doctype)}); - } - - $(row) - .append(frappe.render_template("medical_record_row", data)) - .find("a").addClass("grey"); - }, - scrub_data: function(data) { - data.by = frappe.user.full_name(data.owner); - data.imgsrc = frappe.utils.get_file_link(frappe.user_info(data.owner).image); - - data.icon = "icon-flag"; - }, - add_date_separator: function(row, data) { - var date = frappe.datetime.str_to_obj(data.creation); - var last = frappe.medical_record.last_feed_date; - - if((last && frappe.datetime.obj_to_str(last) != frappe.datetime.obj_to_str(date)) || (!last)) { - var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); - if(diff < 1) { - var pdate = 'Today'; - } else if(diff < 2) { - pdate = 'Yesterday'; - } else { - pdate = frappe.datetime.global_date_format(date); - } - data.date_sep = pdate; - data.date_class = pdate=='Today' ? "date-indicator blue" : "date-indicator"; - } else { - data.date_sep = null; - data.date_class = ""; - } - frappe.medical_record.last_feed_date = date; - } -}); diff --git a/erpnext/healthcare/page/medical_record/medical_record.py b/erpnext/healthcare/page/medical_record/medical_record.py deleted file mode 100644 index 22c5852b05..0000000000 --- a/erpnext/healthcare/page/medical_record/medical_record.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, ESS LLP and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.utils import cint - -@frappe.whitelist() -def get_feed(start, page_length, name): - """get feed""" - result = frappe.db.sql("""select name, owner, modified, creation, - reference_doctype, reference_name, subject - from `tabPatient Medical Record` - where patient=%(patient)s - order by creation desc - limit %(start)s, %(page_length)s""", - { - "start": cint(start), - "page_length": cint(page_length), - "patient": name - }, as_dict=True) - - return result diff --git a/erpnext/healthcare/page/medical_record/medical_record_row.html b/erpnext/healthcare/page/medical_record/medical_record_row.html deleted file mode 100644 index 9a670c9aa7..0000000000 --- a/erpnext/healthcare/page/medical_record/medical_record_row.html +++ /dev/null @@ -1,21 +0,0 @@ -
-
- {%= date_sep || "" %} -
-
- -
- -
- - {% if (reference_doctype && reference_name) { %} - {%= __("{0}: {1}", [link, "" + subject + ""]) %} - {% } else { %} - {%= subject %} - {% } %} - -
-
diff --git a/erpnext/healthcare/page/medical_record/patient_select.html b/erpnext/healthcare/page/medical_record/patient_select.html deleted file mode 100644 index 321baf738d..0000000000 --- a/erpnext/healthcare/page/medical_record/patient_select.html +++ /dev/null @@ -1,5 +0,0 @@ -
- -

{%= __("Select Patient") %}

-

-
diff --git a/erpnext/healthcare/page/patient_history/__init__.py b/erpnext/healthcare/page/patient_history/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/healthcare/page/medical_record/medical_record.css b/erpnext/healthcare/page/patient_history/patient_history.css similarity index 62% rename from erpnext/healthcare/page/medical_record/medical_record.css rename to erpnext/healthcare/page/patient_history/patient_history.css index 977625bbd1..865d6abee0 100644 --- a/erpnext/healthcare/page/medical_record/medical_record.css +++ b/erpnext/healthcare/page/patient_history/patient_history.css @@ -14,6 +14,10 @@ margin-bottom: -4px; } +.medical_record-row > * { + z-index: -999; +} + .date-indicator { background:none; font-size:12px; @@ -35,6 +39,61 @@ color: #5e64ff; } +.div-bg-color { + background: #fafbfc; +} + +.bg-color-white { + background: #FFFFFF; +} + +.d-flex { + display: flex; +} + +.width-full { + width: 100%; +} + +.p-3 { + padding: 16px; +} + +.mt-2 { + margin-top: 8px; +} + +.mr-3 { + margin-right: 16px; +} + +.Box { + background-color: #fff; + border: 1px solid #d1d5da; + border-radius: 3px; +} + +.flex-column { + flex-direction: column; +} + +.avatar { + display: inline-block; + overflow: hidden; + line-height: 1; + vertical-align: middle; + border-radius: 3px; +} + +.py-3 { + padding-top: 16px; + padding-bottom: 16px; +} + +.border-bottom { + border-bottom: 1px #e1e4e8 solid; +} + .date-indicator.blue::after { background: #5e64ff; } @@ -65,8 +124,3 @@ #page-medical_record .list-filters { display: none ; } - -#page-medical_record .octicon-heart { - color: #ff5858; - margin: 0px 5px; -} diff --git a/erpnext/healthcare/page/patient_history/patient_history.html b/erpnext/healthcare/page/patient_history/patient_history.html new file mode 100644 index 0000000000..7a9446dffd --- /dev/null +++ b/erpnext/healthcare/page/patient_history/patient_history.html @@ -0,0 +1,20 @@ +
+
+

{%= __("Select Patient") %}

+

+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
\ No newline at end of file diff --git a/erpnext/healthcare/page/patient_history/patient_history.js b/erpnext/healthcare/page/patient_history/patient_history.js new file mode 100644 index 0000000000..87fe7edd29 --- /dev/null +++ b/erpnext/healthcare/page/patient_history/patient_history.js @@ -0,0 +1,300 @@ +frappe.provide("frappe.patient_history"); +frappe.pages['patient_history'].on_page_load = function(wrapper) { + var me = this; + var page = frappe.ui.make_app_page({ + parent: wrapper, + title: 'Patient History', + single_column: true + }); + + frappe.breadcrumbs.add("Healthcare"); + let pid = ''; + page.main.html(frappe.render_template("patient_history", {})); + var patient = frappe.ui.form.make_control({ + parent: page.main.find(".patient"), + df: { + fieldtype: "Link", + options: "Patient", + fieldname: "patient", + change: function(){ + if(pid != patient.get_value() && patient.get_value()){ + me.start = 0; + me.page.main.find(".patient_documents_list").html(""); + get_documents(patient.get_value(), me); + show_patient_info(patient.get_value(), me); + show_patient_vital_charts(patient.get_value(), me, "bp", "mmHg", "Blood Pressure"); + } + pid = patient.get_value(); + } + }, + only_input: true, + }); + patient.refresh(); + + if (frappe.route_options){ + patient.set_value(frappe.route_options.patient); + } + + this.page.main.on("click", ".btn-show-chart", function() { + var btn_show_id = $(this).attr("data-show-chart-id"), pts = $(this).attr("data-pts"); + var title = $(this).attr("data-title"); + show_patient_vital_charts(patient.get_value(), me, btn_show_id, pts, title); + }); + + this.page.main.on("click", ".btn-more", function() { + var doctype = $(this).attr("data-doctype"), docname = $(this).attr("data-docname"); + if(me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched') == "1"){ + me.page.main.find("."+docname).hide(); + me.page.main.find("."+docname).parent().find('.document-html').show(); + }else{ + if(doctype && docname){ + let exclude = ["patient", "patient_name", 'patient_sex', "encounter_date"]; + frappe.call({ + method: "erpnext.healthcare.utils.render_doc_as_html", + args:{ + doctype: doctype, + docname: docname, + exclude_fields: exclude + }, + callback: function(r) { + if (r.message){ + me.page.main.find("."+docname).hide(); + me.page.main.find("."+docname).parent().find('.document-html').html(r.message.html+"\ +
"); + me.page.main.find("."+docname).parent().find('.document-html').show(); + me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched', "1"); + } + }, + freeze: true + }); + } + } + }); + + this.page.main.on("click", ".btn-less", function() { + var docname = $(this).attr("data-docname"); + me.page.main.find("."+docname).parent().find('.document-id').show(); + me.page.main.find("."+docname).parent().find('.document-html').hide(); + }); + me.start = 0; + me.page.main.on("click", ".btn-get-records", function(){ + get_documents(patient.get_value(), me); + }); +}; + +var get_documents = function(patient, me){ + frappe.call({ + "method": "erpnext.healthcare.page.patient_history.patient_history.get_feed", + args: { + name: patient, + start: me.start, + page_length: 20 + }, + callback: function (r) { + var data = r.message; + if(data.length){ + add_to_records(me, data); + }else{ + me.page.main.find(".patient_documents_list").append("


No more records..

"); + me.page.main.find(".btn-get-records").hide(); + } + } + }); +}; + +var add_to_records = function(me, data){ + var details = ""; + me.page.main.find(".patient_documents_list").append(details); + me.start += data.length; + if(data.length===20){ + me.page.main.find(".btn-get-records").show(); + }else{ + me.page.main.find(".btn-get-records").hide(); + me.page.main.find(".patient_documents_list").append("


No more records..

"); + } +}; + +var add_date_separator = function(data) { + var date = frappe.datetime.str_to_obj(data.creation); + + var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); + if(diff < 1) { + var pdate = 'Today'; + } else if(diff < 2) { + pdate = 'Yesterday'; + } else { + pdate = frappe.datetime.global_date_format(date); + } + data.date_sep = pdate; + return data; +}; + +var show_patient_info = function(patient, me){ + frappe.call({ + "method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail", + args: { + patient: patient + }, + callback: function (r) { + var data = r.message; + var details = ""; + if(data.image){ + details += "
"; + } + details += "" + data.patient_name +"
" + data.sex; + if(data.email) details += "
" + data.email; + if(data.mobile) details += "
" + data.mobile; + if(data.occupation) details += "

Occupation : " + data.occupation; + if(data.blood_group) details += "
Blood group : " + data.blood_group; + if(data.allergies) details += "

Allergies : "+ data.allergies.replace("\n", "
"); + if(data.medication) details += "
Medication : "+ data.medication.replace("\n", "
"); + if(data.alcohol_current_use) details += "

Alcohol use : "+ data.alcohol_current_use; + if(data.alcohol_past_use) details += "
Alcohol past use : "+ data.alcohol_past_use; + if(data.tobacco_current_use) details += "
Tobacco use : "+ data.tobacco_current_use; + if(data.tobacco_past_use) details += "
Tobacco past use : "+ data.tobacco_past_use; + if(data.medical_history) details += "

Medical history : "+ data.medical_history.replace("\n", "
"); + if(data.surgical_history) details += "
Surgical history : "+ data.surgical_history.replace("\n", "
"); + if(data.surrounding_factors) details += "

Occupational hazards : "+ data.surrounding_factors.replace("\n", "
"); + if(data.other_risk_factors) details += "
Other risk factors : " + data.other_risk_factors.replace("\n", "
"); + if(data.patient_details) details += "

More info : " + data.patient_details.replace("\n", "
"); + + if(details){ + details = "
" + details + "
"; + } + me.page.main.find(".patient_details").html(details); + } + }); +}; + +var show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) { + frappe.call({ + method: "erpnext.healthcare.utils.get_patient_vitals", + args:{ + patient: patient + }, + callback: function(r) { + if (r.message){ + var show_chart_btns_html = ""; + me.page.main.find(".show_chart_btns").html(show_chart_btns_html); + var data = r.message; + let labels = [], datasets = []; + let bp_systolic = [], bp_diastolic = [], temperature = []; + let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = []; + for(var i=0; i (d + '').toUpperCase(), + formatTooltipY: d => d + ' ' + pts, + } + }); + }else{ + me.page.main.find(".patient_vital_charts").html(""); + me.page.main.find(".show_chart_btns").html(""); + } + } + }); +}; diff --git a/erpnext/healthcare/page/medical_record/medical_record.json b/erpnext/healthcare/page/patient_history/patient_history.json similarity index 57% rename from erpnext/healthcare/page/medical_record/medical_record.json rename to erpnext/healthcare/page/patient_history/patient_history.json index ca30c3be29..b3892a41c6 100644 --- a/erpnext/healthcare/page/medical_record/medical_record.json +++ b/erpnext/healthcare/page/patient_history/patient_history.json @@ -1,18 +1,21 @@ { "content": null, - "creation": "2016-06-09 11:33:14.025787", + "creation": "2018-08-08 17:09:13.816199", "docstatus": 0, "doctype": "Page", - "icon": "icon-play", + "icon": "", "idx": 0, - "modified": "2018-08-06 11:40:39.705660", + "modified": "2018-08-08 17:09:55.969424", "modified_by": "Administrator", "module": "Healthcare", - "name": "medical_record", + "name": "patient_history", "owner": "Administrator", - "page_name": "medical_record", + "page_name": "patient_history", "restrict_to_domain": "Healthcare", "roles": [ + { + "role": "Healthcare Administrator" + }, { "role": "Physician" } @@ -21,5 +24,5 @@ "standard": "Yes", "style": null, "system_page": 0, - "title": "Medical Record" + "title": "Patient History" } \ No newline at end of file diff --git a/erpnext/healthcare/page/patient_history/patient_history.py b/erpnext/healthcare/page/patient_history/patient_history.py new file mode 100644 index 0000000000..772aa4ef5e --- /dev/null +++ b/erpnext/healthcare/page/patient_history/patient_history.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import cint +from erpnext.healthcare.utils import render_docs_as_html + +@frappe.whitelist() +def get_feed(name, start=0, page_length=20): + """get feed""" + result = frappe.db.sql("""select name, owner, creation, + reference_doctype, reference_name, subject + from `tabPatient Medical Record` + where patient=%(patient)s + order by creation desc + limit %(start)s, %(page_length)s""", + { + "patient": name, + "start": cint(start), + "page_length": cint(page_length) + }, as_dict=True) + return result + +@frappe.whitelist() +def get_feed_for_dt(doctype, docname): + """get feed""" + result = frappe.db.sql("""select name, owner, modified, creation, + reference_doctype, reference_name, subject + from `tabPatient Medical Record` + where reference_name=%(docname)s and reference_doctype=%(doctype)s + order by creation desc""", + { + "docname": docname, + "doctype": doctype + }, as_dict=True) + + return result diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 6a226d9c6b..97bb98f677 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -429,3 +429,116 @@ def get_children(doctype, parent, company, is_root=False): occupancy_msg = str(occupied) + " Occupied out of " + str(occupancy_total) each["occupied_out_of_vacant"] = occupancy_msg return hc_service_units + +@frappe.whitelist() +def get_patient_vitals(patient, from_date=None, to_date=None): + if not patient: return + vitals = frappe.db.sql("""select * from `tabVital Signs` where \ + docstatus=1 and patient=%s order by signs_date, signs_time""", \ + (patient), as_dict=1) + if vitals and vitals[0]: + return vitals + else: + return False + +@frappe.whitelist() +def render_docs_as_html(docs): + # docs key value pair {doctype: docname} + docs_html = "
" + for doc in docs: + docs_html += render_doc_as_html(doc['doctype'], doc['docname'])['html'] + "
" + return {'html': docs_html} + +@frappe.whitelist() +def render_doc_as_html(doctype, docname, exclude_fields = []): + #render document as html, three column layout will break + doc = frappe.get_doc(doctype, docname) + meta = frappe.get_meta(doctype) + doc_html = "
" + section_html = "" + section_label = "" + html = "" + sec_on = False + col_on = 0 + has_data = False + for df in meta.fields: + #on section break append append previous section and html to doc html + if df.fieldtype == "Section Break": + if has_data and col_on and sec_on: + doc_html += section_html + html + "
" + elif has_data and not col_on and sec_on: + doc_html += "
" \ + + section_html + html +"
" + while col_on: + doc_html += "
" + col_on -= 1 + sec_on = True + has_data= False + col_on = 0 + section_html = "" + html = "" + if df.label: + section_label = df.label + continue + #on column break append html to section html or doc html + if df.fieldtype == "Column Break": + if sec_on and has_data: + section_html += "
" + section_label + "" + html + "
" + elif has_data: + doc_html += "
" + html + "
" + elif sec_on and not col_on: + section_html += "
" + html = "" + col_on += 1 + if df.label: + html += '
' + df.label + continue + #on table iterate in items and create table based on in_list_view, append to section html or doc html + if df.fieldtype == "Table": + items = doc.get(df.fieldname) + if not items: continue + child_meta = frappe.get_meta(df.options) + if not has_data : has_data = True + table_head = "" + table_row = "" + create_head = True + for item in items: + table_row += '' + for cdf in child_meta.fields: + if cdf.in_list_view: + if create_head: + table_head += '' + cdf.label + '' + if item.get(cdf.fieldname): + table_row += '' + str(item.get(cdf.fieldname)) \ + + '' + else: + table_row += '' + create_head = False + table_row += '' + if sec_on: + section_html += '' + table_head + table_row + '
' + else: + html += '' \ + + table_head + table_row + '
' + continue + #on other field types add label and value to html + if not df.hidden and not df.print_hide and doc.get(df.fieldname) and df.fieldname not in exclude_fields: + html += "
{0} : {1}".format(df.label or df.fieldname, \ + doc.get(df.fieldname)) + if not has_data : has_data = True + if sec_on and col_on and has_data: + doc_html += section_html + html + "
" + elif sec_on and not col_on and has_data: + doc_html += "
" \ + + section_html + html +"
" + if doc_html: + doc_html = "
" %(doctype, docname) + doc_html + "
" + + return {'html': doc_html} diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 571c2dc75b..a29378547e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -605,6 +605,7 @@ erpnext.patches.v11_1.delete_scheduling_tool erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019 execute:frappe.delete_doc_if_exists("Page", "support-analytics") erpnext.patches.v12_0.make_item_manufacturer +erpnext.patches.v12_0.remove_patient_medical_record_page erpnext.patches.v11_1.move_customer_lead_to_dynamic_column erpnext.patches.v11_1.set_default_action_for_quality_inspection erpnext.patches.v11_1.delete_bom_browser diff --git a/erpnext/patches/v12_0/remove_patient_medical_record_page.py b/erpnext/patches/v12_0/remove_patient_medical_record_page.py new file mode 100644 index 0000000000..904bfe4bf1 --- /dev/null +++ b/erpnext/patches/v12_0/remove_patient_medical_record_page.py @@ -0,0 +1,7 @@ +# Copyright (c) 2019 + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.delete_doc("Page", "medical_record") From 64897d26a406804ad1858ba943de4e0e8ead9f91 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 12 Jul 2019 12:55:28 +0530 Subject: [PATCH 75/83] fix(minor): open colour status for issue --- erpnext/support/doctype/issue/issue_list.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/issue/issue_list.js b/erpnext/support/doctype/issue/issue_list.js index e87c464d35..6d702f6bdf 100644 --- a/erpnext/support/doctype/issue/issue_list.js +++ b/erpnext/support/doctype/issue/issue_list.js @@ -23,8 +23,8 @@ frappe.listview_settings['Issue'] = { 'Low': 'yellow', 'Medium': 'orange', 'High': 'red' - } - return [__(doc.status), color[doc.priority] || 'Red', `status,=,Open`]; + }; + return [__(doc.status), color[doc.priority] || 'red', `status,=,Open`]; } else if (doc.status === 'Closed') { return [__(doc.status), "green", "status,=," + doc.status]; } else { From 70dbca22630fc1965bf68235c89c144e23ded6ae Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Fri, 12 Jul 2019 14:29:20 +0530 Subject: [PATCH 76/83] fix: enable track changes for issue doctype (#18290) --- erpnext/support/doctype/issue/issue.json | 765 ++++++++++++----------- 1 file changed, 383 insertions(+), 382 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 41fe380e75..1e5d5f9ea7 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -1,383 +1,384 @@ { - "allow_import": 1, - "allow_rename": 1, - "autoname": "naming_series:", - "creation": "2013-02-01 10:36:25", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "subject_section", - "naming_series", - "subject", - "customer", - "raised_by", - "cb00", - "status", - "priority", - "issue_type", - "sb_details", - "description", - "service_level_section", - "service_level_agreement", - "response_by", - "response_by_variance", - "reset_service_level_agreement", - "cb", - "agreement_fulfilled", - "resolution_by", - "resolution_by_variance", - "service_level_agreement_creation", - "response", - "mins_to_first_response", - "first_responded_on", - "additional_info", - "lead", - "contact", - "email_account", - "column_break_16", - "customer_name", - "project", - "company", - "section_break_19", - "resolution_details", - "column_break1", - "opening_date", - "opening_time", - "resolution_date", - "content_type", - "attachment", - "via_customer_portal" - ], - "fields": [ - { - "fieldname": "subject_section", - "fieldtype": "Section Break", - "label": "Subject", - "options": "fa fa-flag" - }, - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "no_copy": 1, - "options": "ISS-.YYYY.-", - "print_hide": 1, - "set_only_once": 1 - }, - { - "bold": 1, - "fieldname": "subject", - "fieldtype": "Data", - "in_global_search": 1, - "in_standard_filter": 1, - "label": "Subject", - "reqd": 1 - }, - { - "fieldname": "customer", - "fieldtype": "Link", - "in_global_search": 1, - "label": "Customer", - "oldfieldname": "customer", - "oldfieldtype": "Link", - "options": "Customer", - "print_hide": 1, - "search_index": 1 - }, - { - "bold": 1, - "depends_on": "eval:doc.__islocal", - "fieldname": "raised_by", - "fieldtype": "Data", - "in_global_search": 1, - "in_list_view": 1, - "label": "Raised By (Email)", - "oldfieldname": "raised_by", - "oldfieldtype": "Data", - "options": "Email" - }, - { - "fieldname": "cb00", - "fieldtype": "Column Break" - }, - { - "default": "Open", - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Status", - "no_copy": 1, - "oldfieldname": "status", - "oldfieldtype": "Select", - "options": "Open\nReplied\nHold\nClosed", - "search_index": 1 - }, - { - "default": "Medium", - "fieldname": "priority", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Priority", - "options": "Issue Priority" - }, - { - "fieldname": "issue_type", - "fieldtype": "Link", - "label": "Issue Type", - "options": "Issue Type" - }, - { - "collapsible": 1, - "collapsible_depends_on": "eval:doc.status!=\"Closed\"", - "fieldname": "sb_details", - "fieldtype": "Section Break", - "label": "Details" - }, - { - "bold": 1, - "fieldname": "description", - "fieldtype": "Text Editor", - "in_global_search": 1, - "label": "Description", - "oldfieldname": "problem_description", - "oldfieldtype": "Text" - }, - { - "collapsible": 1, - "fieldname": "service_level_section", - "fieldtype": "Section Break", - "label": "Service Level" - }, - { - "fieldname": "service_level_agreement", - "fieldtype": "Link", - "label": "Service Level Agreement", - "options": "Service Level Agreement" - }, - { - "fieldname": "response_by", - "fieldtype": "Datetime", - "label": "Response By", - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "cb", - "fieldtype": "Column Break", - "options": "fa fa-pushpin", - "read_only": 1 - }, - { - "fieldname": "resolution_by", - "fieldtype": "Datetime", - "label": "Resolution By", - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "response", - "fieldtype": "Section Break", - "label": "Response" - }, - { - "bold": 1, - "fieldname": "mins_to_first_response", - "fieldtype": "Float", - "label": "Mins to First Response", - "read_only": 1 - }, - { - "fieldname": "first_responded_on", - "fieldtype": "Datetime", - "label": "First Responded On" - }, - { - "collapsible": 1, - "fieldname": "additional_info", - "fieldtype": "Section Break", - "label": "Reference", - "options": "fa fa-pushpin", - "read_only": 1 - }, - { - "fieldname": "lead", - "fieldtype": "Link", - "label": "Lead", - "options": "Lead" - }, - { - "fieldname": "contact", - "fieldtype": "Link", - "label": "Contact", - "options": "Contact" - }, - { - "fieldname": "email_account", - "fieldtype": "Link", - "label": "Email Account", - "options": "Email Account" - }, - { - "fieldname": "column_break_16", - "fieldtype": "Column Break" - }, - { - "bold": 1, - "fieldname": "customer_name", - "fieldtype": "Data", - "label": "Customer Name", - "oldfieldname": "customer_name", - "oldfieldtype": "Data", - "read_only": 1 - }, - { - "fieldname": "project", - "fieldtype": "Link", - "label": "Project", - "options": "Project" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "print_hide": 1 - }, - { - "collapsible": 1, - "fieldname": "section_break_19", - "fieldtype": "Section Break", - "label": "Resolution" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "resolution_details", - "fieldtype": "Text Editor", - "label": "Resolution Details", - "no_copy": 1, - "oldfieldname": "resolution_details", - "oldfieldtype": "Text" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "column_break1", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "read_only": 1 - }, - { - "default": "Today", - "fieldname": "opening_date", - "fieldtype": "Date", - "label": "Opening Date", - "no_copy": 1, - "oldfieldname": "opening_date", - "oldfieldtype": "Date", - "read_only": 1 - }, - { - "fieldname": "opening_time", - "fieldtype": "Time", - "label": "Opening Time", - "no_copy": 1, - "oldfieldname": "opening_time", - "oldfieldtype": "Time", - "read_only": 1 - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "resolution_date", - "fieldtype": "Datetime", - "label": "Resolution Date", - "no_copy": 1, - "oldfieldname": "resolution_date", - "oldfieldtype": "Date", - "read_only": 1 - }, - { - "fieldname": "content_type", - "fieldtype": "Data", - "hidden": 1, - "label": "Content Type" - }, - { - "fieldname": "attachment", - "fieldtype": "Attach", - "hidden": 1, - "label": "Attachment" - }, - { - "default": "0", - "fieldname": "via_customer_portal", - "fieldtype": "Check", - "label": "Via Customer Portal" - }, - { - "default": "Ongoing", - "depends_on": "eval: doc.service_level_agreement", - "fieldname": "agreement_fulfilled", - "fieldtype": "Select", - "label": "Service Level Agreement Fulfilled", - "options": "Ongoing\nFulfilled\nFailed", - "read_only": 1 - }, - { - "depends_on": "eval: doc.service_level_agreement", - "description": "in hours", - "fieldname": "response_by_variance", - "fieldtype": "Float", - "label": "Response By Variance", - "read_only": 1 - }, - { - "depends_on": "eval: doc.service_level_agreement", - "description": "in hours", - "fieldname": "resolution_by_variance", - "fieldtype": "Float", - "label": "Resolution By Variance", - "read_only": 1 - }, - { - "fieldname": "service_level_agreement_creation", - "fieldtype": "Datetime", - "hidden": 1, - "label": "Service Level Agreement Creation", - "read_only": 1 - }, - { - "depends_on": "eval: doc.service_level_agreement", - "fieldname": "reset_service_level_agreement", - "fieldtype": "Button", - "label": "Reset Service Level Agreement" - } - ], - "icon": "fa fa-ticket", - "idx": 7, - "modified": "2019-07-11 23:57:22.015881", - "modified_by": "Administrator", - "module": "Support", - "name": "Issue", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Support Team", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "search_fields": "status,customer,subject,raised_by", - "sort_field": "modified", - "sort_order": "ASC", - "timeline_field": "customer", - "title_field": "subject", - "track_seen": 1 -} \ No newline at end of file + "allow_import": 1, + "allow_rename": 1, + "autoname": "naming_series:", + "creation": "2013-02-01 10:36:25", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "subject_section", + "naming_series", + "subject", + "customer", + "raised_by", + "cb00", + "status", + "priority", + "issue_type", + "sb_details", + "description", + "service_level_section", + "service_level_agreement", + "response_by", + "response_by_variance", + "reset_service_level_agreement", + "cb", + "agreement_fulfilled", + "resolution_by", + "resolution_by_variance", + "service_level_agreement_creation", + "response", + "mins_to_first_response", + "first_responded_on", + "additional_info", + "lead", + "contact", + "email_account", + "column_break_16", + "customer_name", + "project", + "company", + "section_break_19", + "resolution_details", + "column_break1", + "opening_date", + "opening_time", + "resolution_date", + "content_type", + "attachment", + "via_customer_portal" + ], + "fields": [ + { + "fieldname": "subject_section", + "fieldtype": "Section Break", + "label": "Subject", + "options": "fa fa-flag" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "ISS-.YYYY.-", + "print_hide": 1, + "set_only_once": 1 + }, + { + "bold": 1, + "fieldname": "subject", + "fieldtype": "Data", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Subject", + "reqd": 1 + }, + { + "fieldname": "customer", + "fieldtype": "Link", + "in_global_search": 1, + "label": "Customer", + "oldfieldname": "customer", + "oldfieldtype": "Link", + "options": "Customer", + "print_hide": 1, + "search_index": 1 + }, + { + "bold": 1, + "depends_on": "eval:doc.__islocal", + "fieldname": "raised_by", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Raised By (Email)", + "oldfieldname": "raised_by", + "oldfieldtype": "Data", + "options": "Email" + }, + { + "fieldname": "cb00", + "fieldtype": "Column Break" + }, + { + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "oldfieldname": "status", + "oldfieldtype": "Select", + "options": "Open\nReplied\nHold\nClosed", + "search_index": 1 + }, + { + "default": "Medium", + "fieldname": "priority", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Priority", + "options": "Issue Priority" + }, + { + "fieldname": "issue_type", + "fieldtype": "Link", + "label": "Issue Type", + "options": "Issue Type" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:doc.status!=\"Closed\"", + "fieldname": "sb_details", + "fieldtype": "Section Break", + "label": "Details" + }, + { + "bold": 1, + "fieldname": "description", + "fieldtype": "Text Editor", + "in_global_search": 1, + "label": "Description", + "oldfieldname": "problem_description", + "oldfieldtype": "Text" + }, + { + "collapsible": 1, + "fieldname": "service_level_section", + "fieldtype": "Section Break", + "label": "Service Level" + }, + { + "fieldname": "service_level_agreement", + "fieldtype": "Link", + "label": "Service Level Agreement", + "options": "Service Level Agreement" + }, + { + "fieldname": "response_by", + "fieldtype": "Datetime", + "label": "Response By", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "cb", + "fieldtype": "Column Break", + "options": "fa fa-pushpin", + "read_only": 1 + }, + { + "fieldname": "resolution_by", + "fieldtype": "Datetime", + "label": "Resolution By", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "response", + "fieldtype": "Section Break", + "label": "Response" + }, + { + "bold": 1, + "fieldname": "mins_to_first_response", + "fieldtype": "Float", + "label": "Mins to First Response", + "read_only": 1 + }, + { + "fieldname": "first_responded_on", + "fieldtype": "Datetime", + "label": "First Responded On" + }, + { + "collapsible": 1, + "fieldname": "additional_info", + "fieldtype": "Section Break", + "label": "Reference", + "options": "fa fa-pushpin", + "read_only": 1 + }, + { + "fieldname": "lead", + "fieldtype": "Link", + "label": "Lead", + "options": "Lead" + }, + { + "fieldname": "contact", + "fieldtype": "Link", + "label": "Contact", + "options": "Contact" + }, + { + "fieldname": "email_account", + "fieldtype": "Link", + "label": "Email Account", + "options": "Email Account" + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break" + }, + { + "bold": 1, + "fieldname": "customer_name", + "fieldtype": "Data", + "label": "Customer Name", + "oldfieldname": "customer_name", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "print_hide": 1 + }, + { + "collapsible": 1, + "fieldname": "section_break_19", + "fieldtype": "Section Break", + "label": "Resolution" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "resolution_details", + "fieldtype": "Text Editor", + "label": "Resolution Details", + "no_copy": 1, + "oldfieldname": "resolution_details", + "oldfieldtype": "Text" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "read_only": 1 + }, + { + "default": "Today", + "fieldname": "opening_date", + "fieldtype": "Date", + "label": "Opening Date", + "no_copy": 1, + "oldfieldname": "opening_date", + "oldfieldtype": "Date", + "read_only": 1 + }, + { + "fieldname": "opening_time", + "fieldtype": "Time", + "label": "Opening Time", + "no_copy": 1, + "oldfieldname": "opening_time", + "oldfieldtype": "Time", + "read_only": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "resolution_date", + "fieldtype": "Datetime", + "label": "Resolution Date", + "no_copy": 1, + "oldfieldname": "resolution_date", + "oldfieldtype": "Date", + "read_only": 1 + }, + { + "fieldname": "content_type", + "fieldtype": "Data", + "hidden": 1, + "label": "Content Type" + }, + { + "fieldname": "attachment", + "fieldtype": "Attach", + "hidden": 1, + "label": "Attachment" + }, + { + "default": "0", + "fieldname": "via_customer_portal", + "fieldtype": "Check", + "label": "Via Customer Portal" + }, + { + "default": "Ongoing", + "depends_on": "eval: doc.service_level_agreement", + "fieldname": "agreement_fulfilled", + "fieldtype": "Select", + "label": "Service Level Agreement Fulfilled", + "options": "Ongoing\nFulfilled\nFailed", + "read_only": 1 + }, + { + "depends_on": "eval: doc.service_level_agreement", + "description": "in hours", + "fieldname": "response_by_variance", + "fieldtype": "Float", + "label": "Response By Variance", + "read_only": 1 + }, + { + "depends_on": "eval: doc.service_level_agreement", + "description": "in hours", + "fieldname": "resolution_by_variance", + "fieldtype": "Float", + "label": "Resolution By Variance", + "read_only": 1 + }, + { + "fieldname": "service_level_agreement_creation", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Service Level Agreement Creation", + "read_only": 1 + }, + { + "depends_on": "eval: doc.service_level_agreement", + "fieldname": "reset_service_level_agreement", + "fieldtype": "Button", + "label": "Reset Service Level Agreement" + } + ], + "icon": "fa fa-ticket", + "idx": 7, + "modified": "2019-07-11 23:57:22.015881", + "modified_by": "Administrator", + "module": "Support", + "name": "Issue", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Support Team", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "search_fields": "status,customer,subject,raised_by", + "sort_field": "modified", + "sort_order": "ASC", + "timeline_field": "customer", + "title_field": "subject", + "track_changes": 1, + "track_seen": 1 + } \ No newline at end of file From b315acb5a7b4c77f7ff5ca677d2c3c6854e13abe Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 12 Jul 2019 14:27:19 +0530 Subject: [PATCH 77/83] fix: Calculate taxes and totals only if items added in transaction --- erpnext/controllers/taxes_and_totals.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index ebbe3d9d27..8e1510a836 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -15,6 +15,9 @@ class calculate_taxes_and_totals(object): self.calculate() def calculate(self): + if not len(self.doc.get("items")): + return + self.discount_amount_applied = False self._calculate() From d73426a3bc0d03bfd6f44d4afd5212134b577c85 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 12 Jul 2019 14:44:22 +0530 Subject: [PATCH 78/83] fix: Add status field at parent level in quality inspection and minor UX fix (#18263) * fix: Add status field at parent level and minor UX fix * fix: Set default status as Accepted --- .../quality_inspection.json | 1049 ++++------------- .../quality_inspection_reading.json | 517 ++------ 2 files changed, 347 insertions(+), 1219 deletions(-) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json index 94d47947ef..a9f3cd09ef 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -1,857 +1,260 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2013-04-30 13:13:03", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", + "autoname": "naming_series:", + "creation": "2013-04-30 13:13:03", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "report_date", + "column_break_4", + "inspection_type", + "reference_type", + "reference_name", + "section_break_7", + "item_code", + "item_serial_no", + "batch_no", + "sample_size", + "column_break1", + "item_name", + "description", + "status", + "section_break_14", + "inspected_by", + "verified_by", + "bom_no", + "column_break_17", + "remarks", + "amended_from", + "specification_details", + "quality_inspection_template", + "readings" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 1, - "options": "MAT-QA-.YYYY.-", - "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": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "MAT-QA-.YYYY.-", + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "report_date", - "fieldtype": "Date", - "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": "Report Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "report_date", - "oldfieldtype": "Date", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "report_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Report Date", + "oldfieldname": "report_date", + "oldfieldtype": "Date", + "reqd": 1, + "search_index": 1 + }, { - "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, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "inspection_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Inspection Type", - "length": 0, - "no_copy": 0, - "oldfieldname": "inspection_type", - "oldfieldtype": "Select", - "options": "\nIncoming\nOutgoing\nIn Process", - "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 - }, + "fieldname": "inspection_type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Inspection Type", + "oldfieldname": "inspection_type", + "oldfieldtype": "Select", + "options": "\nIncoming\nOutgoing\nIn Process", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_type", - "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": "Reference Type", - "length": 0, - "no_copy": 0, - "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_type", + "fieldtype": "Select", + "label": "Reference Type", + "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "reference_name", - "fieldtype": "Dynamic 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": "Reference Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "purchase_receipt_no", - "oldfieldtype": "Link", - "options": "reference_type", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Reference Name", + "oldfieldname": "purchase_receipt_no", + "oldfieldtype": "Link", + "options": "reference_type" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_code", - "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 Code", - "length": 0, - "no_copy": 0, - "oldfieldname": "item_code", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Item Code", + "oldfieldname": "item_code", + "oldfieldtype": "Link", + "options": "Item", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_serial_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": "Item Serial No", - "length": 0, - "no_copy": 0, - "oldfieldname": "item_serial_no", - "oldfieldtype": "Link", - "options": "Serial No", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_serial_no", + "fieldtype": "Link", + "label": "Item Serial No", + "oldfieldname": "item_serial_no", + "oldfieldtype": "Link", + "options": "Serial No" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "batch_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": "Batch No", - "length": 0, - "no_copy": 0, - "oldfieldname": "batch_no", - "oldfieldtype": "Link", - "options": "Batch", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "batch_no", + "fieldtype": "Link", + "label": "Batch No", + "oldfieldname": "batch_no", + "oldfieldtype": "Link", + "options": "Batch" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample_size", - "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": "Sample Size", - "length": 0, - "no_copy": 0, - "oldfieldname": "sample_size", - "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 - }, + "fieldname": "sample_size", + "fieldtype": "Float", + "label": "Sample Size", + "oldfieldname": "sample_size", + "oldfieldtype": "Currency", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "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 - }, + "fieldname": "item_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Item Name", + "read_only": 1 + }, { - "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": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Small Text", "width": "300px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_14", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_14", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "user", - "fieldname": "inspected_by", - "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": "Inspected By", - "length": 0, - "no_copy": 0, - "oldfieldname": "inspected_by", - "oldfieldtype": "Data", - "options": "User", - "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 - }, + "default": "user", + "fieldname": "inspected_by", + "fieldtype": "Link", + "label": "Inspected By", + "oldfieldname": "inspected_by", + "oldfieldtype": "Data", + "options": "User", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "verified_by", - "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": "Verified By", - "length": 0, - "no_copy": 0, - "oldfieldname": "verified_by", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "verified_by", + "fieldtype": "Data", + "label": "Verified By", + "oldfieldname": "verified_by", + "oldfieldtype": "Data" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "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, - "options": "BOM", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bom_no", + "fieldtype": "Link", + "label": "BOM No", + "options": "BOM", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_17", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "remarks", - "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": "Remarks", - "length": 0, - "no_copy": 1, - "oldfieldname": "remarks", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "remarks", + "fieldtype": "Text", + "label": "Remarks", + "no_copy": 1, + "oldfieldname": "remarks", + "oldfieldtype": "Text" + }, { - "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": "Quality Inspection", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Quality Inspection", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "specification_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "Simple", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "specification_details", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "Simple" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "quality_inspection_template", - "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": "Quality Inspection Template", - "length": 0, - "no_copy": 0, - "options": "Quality Inspection Template", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "quality_inspection_template", + "fieldtype": "Link", + "label": "Quality Inspection Template", + "options": "Quality Inspection Template" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "readings", - "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": "Readings", - "length": 0, - "no_copy": 0, - "oldfieldname": "qa_specification_details", - "oldfieldtype": "Table", - "options": "Quality Inspection Reading", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "readings", + "fieldtype": "Table", + "label": "Readings", + "oldfieldname": "qa_specification_details", + "oldfieldtype": "Table", + "options": "Quality Inspection Reading" + }, + { + "default": "Accepted", + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "\nAccepted\nRejected", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-search", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:45.996446", - "modified_by": "Administrator", - "module": "Stock", - "name": "Quality Inspection", - "owner": "Administrator", + ], + "icon": "fa fa-search", + "idx": 1, + "is_submittable": 1, + "modified": "2019-07-12 12:07:23.153698", + "modified_by": "Administrator", + "module": "Stock", + "name": "Quality Inspection", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Quality Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "item_code, report_date, reference_name", - "show_name_in_global_search": 1, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "search_fields": "item_code, report_date, reference_name", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "ASC" } \ No newline at end of file diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json index 94aecfe7e7..f9f8a71c02 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -1,413 +1,138 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "hash", - "beta": 0, - "creation": "2013-02-22 01:27:43", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, + "autoname": "hash", + "creation": "2013-02-22 01:27:43", + "doctype": "DocType", + "editable_grid": 1, + "field_order": [ + "specification", + "value", + "reading_1", + "reading_2", + "reading_3", + "reading_4", + "reading_5", + "reading_6", + "reading_7", + "reading_8", + "reading_9", + "reading_10", + "status" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "specification", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Parameter", - "length": 0, - "no_copy": 0, - "oldfieldname": "specification", - "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, - "unique": 0 - }, + "columns": 3, + "fieldname": "specification", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Parameter", + "oldfieldname": "specification", + "oldfieldtype": "Data", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "value", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Acceptance Criteria", - "length": 0, - "no_copy": 0, - "oldfieldname": "value", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "value", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Acceptance Criteria", + "oldfieldname": "value", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_1", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reading 1", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_1", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 1, + "fieldname": "reading_1", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Reading 1", + "oldfieldname": "reading_1", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_2", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reading 2", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_2", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 1, + "fieldname": "reading_2", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Reading 2", + "oldfieldname": "reading_2", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_3", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reading 3", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_3", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 1, + "fieldname": "reading_3", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Reading 3", + "oldfieldname": "reading_3", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_4", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reading 4", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_4", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "reading_4", + "fieldtype": "Data", + "label": "Reading 4", + "oldfieldname": "reading_4", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_5", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reading 5", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_5", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "reading_5", + "fieldtype": "Data", + "label": "Reading 5", + "oldfieldname": "reading_5", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_6", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reading 6", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_6", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "reading_6", + "fieldtype": "Data", + "label": "Reading 6", + "oldfieldname": "reading_6", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_7", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reading 7", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_7", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "reading_7", + "fieldtype": "Data", + "label": "Reading 7", + "oldfieldname": "reading_7", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_8", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reading 8", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_8", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "reading_8", + "fieldtype": "Data", + "label": "Reading 8", + "oldfieldname": "reading_8", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_9", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reading 9", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_9", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "reading_9", + "fieldtype": "Data", + "label": "Reading 9", + "oldfieldname": "reading_9", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_10", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reading 10", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_10", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "reading_10", + "fieldtype": "Data", + "label": "Reading 10", + "oldfieldname": "reading_10", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Accepted", - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "oldfieldname": "status", - "oldfieldtype": "Select", - "options": "Accepted\nRejected", - "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, - "unique": 0 + "columns": 2, + "default": "Accepted", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "oldfieldname": "status", + "oldfieldtype": "Select", + "options": "Accepted\nRejected" } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2016-11-16 03:55:50.046712", - "modified_by": "Administrator", - "module": "Stock", - "name": "Quality Inspection Reading", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "track_seen": 0 + ], + "idx": 1, + "istable": 1, + "modified": "2019-07-11 18:48:12.667404", + "modified_by": "Administrator", + "module": "Stock", + "name": "Quality Inspection Reading", + "owner": "Administrator", + "permissions": [], + "track_changes": 1 } \ No newline at end of file From ed9ac14f706ca7ead5bc6931df5ce5ba7d291fd1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 12 Jul 2019 14:47:00 +0530 Subject: [PATCH 79/83] fix: Usability fixes for chart of Accounts Importer (#18285) * fix: Usablity fixes for chart of Accounts Importer * fix: Provide Row no with error messages --- .../chart_of_accounts_importer.js | 13 +------ .../chart_of_accounts_importer.py | 34 +++++++++++++++---- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js index aea6080288..40a97ae295 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js @@ -78,7 +78,6 @@ var validate_csv_data = function(frm) { var create_import_button = function(frm) { frm.page.set_primary_action(__("Start Import"), function () { - setup_progress_bar(frm); frappe.call({ method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa", args: { @@ -86,11 +85,11 @@ var create_import_button = function(frm) { company: frm.doc.company }, freeze: true, + freeze_message: __("Creating Accounts..."), callback: function(r) { if(!r.exc) { clearInterval(frm.page["interval"]); frm.page.set_indicator(__('Import Successfull'), 'blue'); - frappe.hide_progress(); create_reset_button(frm); } } @@ -126,13 +125,3 @@ var generate_tree_preview = function(frm) { } }); }; - -var setup_progress_bar = function(frm) { - frm.page["seconds_elapsed"] = 0; - frm.page["execution_time"] = (frm.page["total_accounts"] > 100) ? 100 : frm.page["total_accounts"]; - - frm.page["interval"] = setInterval(function() { - frm.page["seconds_elapsed"] += 1; - frappe.show_progress(__('Creating Accounts'), frm.page["seconds_elapsed"], frm.page["execution_time"]); - }, 250); -}; \ No newline at end of file diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index 76efa486f8..4683c7ae46 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -33,6 +33,9 @@ def import_coa(file_name, company): def generate_data_from_csv(file_name, as_dict=False): ''' read csv file and return the generated nested tree ''' + if not file_name.endswith('.csv'): + frappe.throw("Only CSV files can be used to for importing data. Please check the file format you are trying to upload") + file_doc = frappe.get_doc('File', {"file_url": file_name}) file_path = file_doc.get_full_path() @@ -96,15 +99,27 @@ def build_forest(data): return [child] + return_parent(data, parent_account) charts_map, paths = {}, [] + + line_no = 3 + error_messages = [] + for i in data: account_name, _, account_number, is_group, account_type, root_type = i + + if not account_name: + error_messages.append("Row {0}: Please enter Account Name".format(line_no)) + charts_map[account_name] = {} - if is_group: charts_map[account_name]["is_group"] = is_group + if is_group == 1: charts_map[account_name]["is_group"] = is_group if account_type: charts_map[account_name]["account_type"] = account_type if root_type: charts_map[account_name]["root_type"] = root_type if account_number: charts_map[account_name]["account_number"] = account_number path = return_parent(data, account_name)[::-1] paths.append(path) # List of path is created + line_no += 1 + + if error_messages: + frappe.throw("
".join(error_messages)) out = {} for path in paths: @@ -150,22 +165,27 @@ def validate_root(accounts): if len(roots) < 4: return _("Number of root accounts cannot be less than 4") + error_messages = [] + for account in roots: - if not account.get("root_type"): - return _("Please enter Root Type for - {0}").format(account.get("account_name")) - elif account.get("root_type") not in ("Asset", "Liability", "Expense", "Income", "Equity"): - return _('Root Type for "{0}" must be one of the Asset, Liability, Income, Expense and Equity').format(account.get("account_name")) + if not account.get("root_type") and account.get("account_name"): + error_messages.append("Please enter Root Type for account- {0}".format(account.get("account_name"))) + elif account.get("root_type") not in ("Asset", "Liability", "Expense", "Income", "Equity") and account.get("account_name"): + error_messages.append("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity".format(account.get("account_name"))) + + if error_messages: + return "
".join(error_messages) def validate_account_types(accounts): account_types_for_ledger = ["Cost of Goods Sold", "Depreciation", "Fixed Asset", "Payable", "Receivable", "Stock Adjustment"] - account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group']] + account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group'] == 1] missing = list(set(account_types_for_ledger) - set(account_types)) if missing: return _("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing)) account_types_for_group = ["Bank", "Cash", "Stock"] - account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group']] + account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group'] not in ('', 1)] missing = list(set(account_types_for_group) - set(account_groups)) if missing: From 397013ec92d0dafc8716e624051b0f5411a51231 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 12 Jul 2019 14:51:01 +0530 Subject: [PATCH 80/83] fix: Fix for customer lead merging in Quotation (#18294) --- erpnext/shopping_cart/cart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 4019e07e4e..62bde420a8 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -61,7 +61,7 @@ def place_order(): quotation.flags.ignore_permissions = True quotation.submit() - if quotation.lead: + if quotation.quotation_to == 'Lead' and quotation.party_name: # company used to create customer accounts frappe.defaults.set_user_default("company", quotation.company) From 8412f847d4ae0e7e8836ca14e4758fcfef012ffa Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 12 Jul 2019 15:44:58 +0530 Subject: [PATCH 81/83] fix: dr / cr label for balance of the ledgers in the account tree --- erpnext/accounts/doctype/account/account_tree.js | 2 +- erpnext/accounts/utils.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index dc4c69d9e8..b70c6d2b27 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -121,7 +121,7 @@ frappe.treeview_settings["Account"] = { }, onrender: function(node) { if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){ - var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr"; + var dr_or_cr = in_list(["Liability", "Income", "Equity"], node.data.root_type) ? "Cr" : "Dr"; if (node.data && node.data.balance!==undefined) { $('' + (node.data.balance_in_account_currency ? diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 542c7e4e52..e75dd957db 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -734,6 +734,7 @@ def get_children(doctype, parent, company, is_root=False): parent_fieldname = 'parent_' + doctype.lower().replace(' ', '_') fields = [ 'name as value', + 'root_type', 'is_group as expandable' ] filters = [['docstatus', '<', 2]] @@ -741,7 +742,7 @@ def get_children(doctype, parent, company, is_root=False): filters.append(['ifnull(`{0}`,"")'.format(parent_fieldname), '=', '' if is_root else parent]) if is_root: - fields += ['root_type', 'report_type', 'account_currency'] if doctype == 'Account' else [] + fields += ['report_type', 'account_currency'] if doctype == 'Account' else [] filters.append(['company', '=', company]) else: From 625ec43fc91f075689f288a7100699eee385aaa6 Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Fri, 12 Jul 2019 15:57:37 +0530 Subject: [PATCH 82/83] fix(auto attendance): Prevent marking attendance for the ongoing shift --- erpnext/hr/doctype/shift_type/shift_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py index eaf6b1e2d3..b98f445c0f 100644 --- a/erpnext/hr/doctype/shift_type/shift_type.py +++ b/erpnext/hr/doctype/shift_type/shift_type.py @@ -22,7 +22,7 @@ class ShiftType(Document): 'skip_auto_attendance':'0', 'attendance':('is', 'not set'), 'time':('>=', self.process_attendance_after), - 'shift_actual_start': ('<', self.last_sync_of_checkin), + 'shift_actual_end': ('<', self.last_sync_of_checkin), 'shift': self.name } logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time") From dc02f4d07d2f002247811f0138755e1f1bc31d16 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Sat, 13 Jul 2019 20:00:25 +0530 Subject: [PATCH 83/83] fix: miscellaneous fixes --- .../quality_feedback_parameter.json | 4 ++-- .../doctype/quality_meeting/quality_meeting.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json b/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json index d0b368f293..5bd8920a32 100644 --- a/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json +++ b/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json @@ -28,7 +28,7 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Rating", - "options": "1\n2\n3\n4\n5", + "options": "\n1\n2\n3\n4\n5", "reqd": 1 }, { @@ -44,7 +44,7 @@ } ], "istable": 1, - "modified": "2019-05-26 21:50:48.951264", + "modified": "2019-07-13 19:58:08.966141", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Feedback Parameter", diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json index 6985632e49..0849fd7aeb 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json @@ -26,7 +26,7 @@ "fieldname": "status", "fieldtype": "Select", "label": "Status", - "options": "Open\nClose" + "options": "Open\nClosed" }, { "fieldname": "minutes", @@ -55,7 +55,7 @@ "label": "Minutes" } ], - "modified": "2019-05-26 23:12:23.364357", + "modified": "2019-07-13 19:57:40.500541", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Meeting",