From f805a76e792ca27392db5695b8ccd7902a6c02cc Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 13 Nov 2019 17:27:16 +0530 Subject: [PATCH 001/929] chore: pinned requirements --- requirements.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/requirements.txt b/requirements.txt index 28ba9f676ff..429f894edce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ frappe -unidecode -pygithub -googlemaps -python-stdnum -braintree -gocardless_pro -woocommerce -pandas -plaid-python \ No newline at end of file +Unidecode==1.1.1 +PyGithub==1.43.8 +googlemaps==3.1.1 +python-stdnum==1.11 +braintree==3.57.1 +gocardless-pro==1.10.0 +WooCommerce==2.1.1 +pandas==0.25.1 +plaid-python==3.4.0 From 3798f8bd25d6ac02c7d09da61e45db8c9a5c51e4 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 15 Nov 2019 13:59:15 +0530 Subject: [PATCH 002/929] style(requirements): alphabetically sorted requirements --- requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 429f894edce..1ab84ad57b5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ -frappe -Unidecode==1.1.1 -PyGithub==1.43.8 -googlemaps==3.1.1 -python-stdnum==1.11 braintree==3.57.1 +frappe gocardless-pro==1.10.0 -WooCommerce==2.1.1 +googlemaps==3.1.1 pandas==0.25.1 plaid-python==3.4.0 +PyGithub==1.43.8 +python-stdnum==1.11 +Unidecode==1.1.1 +WooCommerce==2.1.1 From 73089470b18f2a04a28b3ba3391b1605e12becf4 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 15 Nov 2019 17:38:32 +0530 Subject: [PATCH 003/929] chore: pinned backwards compatible dependencies --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1ab84ad57b5..c277545fab5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ braintree==3.57.1 frappe -gocardless-pro==1.10.0 +gocardless-pro==1.11.0 googlemaps==3.1.1 -pandas==0.25.1 +pandas==0.24.2 plaid-python==3.4.0 -PyGithub==1.43.8 -python-stdnum==1.11 +PyGithub==1.44.1 +python-stdnum==1.12 Unidecode==1.1.1 WooCommerce==2.1.1 From 83ed7df8cdd1332a6029c35473aa3dd40c36fee5 Mon Sep 17 00:00:00 2001 From: thefalconx33 Date: Sat, 11 Jan 2020 14:19:02 +0530 Subject: [PATCH 004/929] fix: auto cancel if movement exists --- erpnext/assets/doctype/asset/asset.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 3e7f6833a0c..86b5a11a1d1 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -132,9 +132,10 @@ class Asset(AccountsController): if len(movements) > 1: frappe.throw(_('Asset has multiple Asset Movement Entries which has to be \ cancelled manually to cancel this asset.')) - movement = frappe.get_doc('Asset Movement', movements[0].get('name')) - movement.flags.ignore_validate = True - movement.cancel() + if movements: + movement = frappe.get_doc('Asset Movement', movements[0].get('name')) + movement.flags.ignore_validate = True + movement.cancel() def make_asset_movement(self): reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice' From e5a572a4a8c914a601c8fde3273a57b48cd74276 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 6 Jan 2020 12:34:53 +0530 Subject: [PATCH 005/929] fix: Group by sales register report --- .../item_wise_sales_register.js | 35 +- .../item_wise_sales_register.py | 335 ++++++++++++++++-- .../report/sales_register/sales_register.py | 4 +- 3 files changed, 330 insertions(+), 44 deletions(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js index 9dd9b3fad48..cff51cb8d64 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js @@ -4,48 +4,67 @@ frappe.query_reports["Item-wise Sales Register"] = { "filters": [ { - "fieldname":"date_range", + "fieldname": "date_range", "label": __("Date Range"), "fieldtype": "DateRange", "default": [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()], "reqd": 1 }, { - "fieldname":"customer", + "fieldname": "customer", "label": __("Customer"), "fieldtype": "Link", "options": "Customer" }, { - "fieldname":"company", + "fieldname": "company", "label": __("Company"), "fieldtype": "Link", "options": "Company", "default": frappe.defaults.get_user_default("Company") }, { - "fieldname":"mode_of_payment", + "fieldname": "mode_of_payment", "label": __("Mode of Payment"), "fieldtype": "Link", "options": "Mode of Payment" }, { - "fieldname":"warehouse", + "fieldname": "warehouse", "label": __("Warehouse"), "fieldtype": "Link", "options": "Warehouse" }, { - "fieldname":"brand", + "fieldname": "brand", "label": __("Brand"), "fieldtype": "Link", "options": "Brand" }, { - "fieldname":"item_group", + "fieldname": "item_group", "label": __("Item Group"), "fieldtype": "Link", "options": "Item Group" + }, + { + "label": __("Group By"), + "fieldname": "group_by", + "fieldtype": "Select", + "options": ["Customer", "Item", "Item Group", "Invoice"] } - ] + ], + "formatter": function(value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (data && data.bold) { + // value = $(`${value}`); + // var $value = $(value).css("font-weight", "bold"); + // $value.addClass("text-danger"); + value = value.bold(); + + } + + return value; + }, } diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 9da89ff2c9f..af6a7b4d62c 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -15,23 +15,31 @@ def execute(filters=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): if not filters: filters = {} filters.update({"from_date": filters.get("date_range") and filters.get("date_range")[0], "to_date": filters.get("date_range") and filters.get("date_range")[1]}) - columns = get_columns(additional_table_columns) + columns = get_columns(additional_table_columns, filters) company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency") item_list = get_items(filters, additional_query_columns) if item_list: itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency) + columns.append({ - "fieldname": "currency", - "label": _("Currency"), - "fieldtype": "Data", - "width": 80 + 'fieldname': 'currency', + 'label': _('Currency'), + 'fieldtype': 'Currency', + 'width': 80, + 'hidden': 1 }) + mode_of_payments = get_mode_of_payments(set([d.parent for d in item_list])) so_dn_map = get_delivery_notes_against_sales_order(item_list) + if filters.get('group_by'): + grand_total = get_grand_total(filters) + data = [] + prev_group_by_value = '' + for d in item_list: delivery_note = None if d.delivery_note: @@ -42,57 +50,271 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum if not delivery_note and d.update_stock: delivery_note = d.parent - row = [d.item_code, d.item_name, d.item_group, d.description, d.parent, d.posting_date, d.customer, d.customer_name] + + if filters.get('group_by'): + if filters.get('group_by') == 'Item': + group_by_field = 'item_code' + elif filters.get('group_by') == 'Invoice': + group_by_field = 'parent' + else: + group_by_field = frappe.scrub(filters.get('group_by')) + + if prev_group_by_value != d.get(group_by_field): + if prev_group_by_value: + total_row['percent_gt'] = flt(total_row['total']/grand_total * 100) + data.append(total_row) + data.append({}) + + prev_group_by_value = d.get(group_by_field) + + total_row = { + group_by_field: d.get(group_by_field), + 'stock_qty': 0.0, + 'amount': 0.0, + 'bold': 1, + 'total_tax': 0.0, + 'total': 0.0 + } + + row = { + 'item_code': d.item_code, + 'item_name': d.item_name, + 'item_group': d.item_group, + 'description': d.description, + 'invoice': d.parent, + 'posting_date': d.posting_date, + 'customer': d.customer, + 'customer_name': d.customer_name + } if additional_query_columns: for col in additional_query_columns: - row.append(d.get(col)) + row.update({ + col: d.get(col) + }) - row += [ - d.customer_group, d.debit_to, ", ".join(mode_of_payments.get(d.parent, [])), - d.territory, d.project, d.company, d.sales_order, - delivery_note, d.income_account, d.cost_center, d.stock_qty, d.stock_uom - ] + row.update({ + 'customer_group': d.customer_group, + 'debit_to': d.debit_to, + 'mode_of_payment': ", ".join(mode_of_payments.get(d.parent, [])), + 'territory': d.territory, + 'project': d.project, + 'company': d.company, + 'sales_order': d.sales_order, + 'delivery_note': d.delivery_note, + 'income_account': d.income_account, + 'cost_center': d.cost_center, + 'stock_qty': d.stock_qty, + 'stock_uom': d.stock_uom + }) if d.stock_uom != d.uom and d.stock_qty: - row += [(d.base_net_rate * d.qty)/d.stock_qty, d.base_net_amount] + row.update({ + 'rate': (d.base_net_rate * d.qty)/d.stock_qty, + 'amount': d.base_net_amount + }) else: - row += [d.base_net_rate, d.base_net_amount] + row.update({ + 'rate': d.base_net_rate, + 'amount': d.base_net_amount + }) total_tax = 0 for tax in tax_columns: item_tax = itemised_tax.get(d.name, {}).get(tax, {}) - row += [item_tax.get("tax_rate", 0), item_tax.get("tax_amount", 0)] + row.update({ + frappe.scrub(tax + ' Rate'): item_tax.get("tax_rate", 0), + frappe.scrub(tax + ' Amount'): item_tax.get("tax_amount", 0), + }) total_tax += flt(item_tax.get("tax_amount")) - row += [total_tax, d.base_net_amount + total_tax, company_currency] + row.update({ + 'total_tax': total_tax, + 'total': d.base_net_amount + total_tax, + 'currency': company_currency + }) + + if filters.get('group_by'): + total_row['stock_qty'] += row['stock_qty'] + total_row['amount'] += row['amount'] + total_row['total_tax'] += row['total_tax'] + total_row['total'] += row['total'] data.append(row) + if filters.get('group_by'): + total_row['percent_gt'] = frappe.bold(total_row['total']/grand_total * 100) + data.append(total_row) + data.append({}) + return columns, data -def get_columns(additional_table_columns): +def get_columns(additional_table_columns, filters): columns = [ - _("Item Code") + ":Link/Item:120", _("Item Name") + "::120", - _("Item Group") + ":Link/Item Group:100", "Description::150", _("Invoice") + ":Link/Sales Invoice:120", - _("Posting Date") + ":Date:80", _("Customer") + ":Link/Customer:120", - _("Customer Name") + "::120"] + { + 'label': _('Item Code'), + 'fieldname': 'item_code', + 'fieldtype': 'Link', + 'options': 'Item', + 'width': 120 + }, + { + 'label': _('Item Name'), + 'fieldname': 'item_name', + 'fieldtype': 'Data', + 'width': 120 + }, + { + 'label': _('Item Group'), + 'fieldname': 'item_group', + 'fieldtype': 'Link', + 'options': 'Item Group', + 'width': 120 + }, + { + 'label': _('Description'), + 'fieldname': 'description', + 'fieldtype': 'Data', + 'width': 150 + }, + { + 'label': _('Invoice'), + 'fieldname': 'invoice', + 'fieldtype': 'Link', + 'options': 'Sales Invoice', + 'width': 120 + }, + { + 'label': _('Posting Date'), + 'fieldname': 'posting_date', + 'fieldtype': 'Date', + 'width': 120 + }, + { + 'label': _('Customer'), + 'fieldname': 'customer', + 'fieldtype': 'Link', + 'options': 'Customer', + 'width': 120 + }, + { + 'label': _('Customer Name'), + 'fieldname': 'customer_name', + 'fieldtype': 'Data', + 'width': 120 + } + ] if additional_table_columns: columns += additional_table_columns columns += [ - _("Customer Group") + ":Link/Customer Group:120", - _("Receivable Account") + ":Link/Account:120", - _("Mode of Payment") + "::120", _("Territory") + ":Link/Territory:80", - _("Project") + ":Link/Project:80", _("Company") + ":Link/Company:100", - _("Sales Order") + ":Link/Sales Order:100", _("Delivery Note") + ":Link/Delivery Note:100", - _("Income Account") + ":Link/Account:140", _("Cost Center") + ":Link/Cost Center:140", - _("Stock Qty") + ":Float:120", _("Stock UOM") + "::100", - _("Rate") + ":Currency/currency:120", - _("Amount") + ":Currency/currency:120" + { + 'label': _('Customer Group'), + 'fieldname': 'customer_group', + 'fieldtype': 'Link', + 'options': 'Customer Group', + 'width': 120 + }, + { + 'label': _('Receivable Account'), + 'fieldname': 'debit_to', + 'fieldtype': 'Link', + 'options': 'Account', + 'width': 80 + }, + { + 'label': _('Mode Of Payment'), + 'fieldname': 'mode_of_payment', + 'fieldtype': 'Data', + 'width': 120 + }, + { + 'label': _("Territory"), + 'fieldname': 'territory', + 'fieldtype': 'Link', + 'options': 'Territory', + 'width': 80 + }, + { + 'label': _('Project'), + 'fieldname': 'project', + 'fieldtype': 'Link', + 'options': 'Project', + 'width': 80 + }, + { + 'label': _('Company'), + 'fieldname': 'company', + 'fieldtype': 'Link', + 'options': 'Company', + 'width': 80 + }, + { + 'label': _('Sales Order'), + 'fieldname': 'sales_order', + 'fieldtype': 'Link', + 'options': 'Sales Order', + 'width': 100 + }, + { + 'label': _("Delivery Note"), + 'fieldname': 'delivery_note', + 'fieldtype': 'Link', + 'options': 'Delivery Note', + 'width': 100 + }, + { + 'label': _('Income Account'), + 'fieldname': 'income_account', + 'fieldtype': 'Link', + 'options': 'Account', + 'width': 100 + }, + { + 'label': _("Cost Center"), + 'fieldname': 'cost_center', + 'fieldtype': 'Link', + 'options': 'Cost Center', + 'width': 100 + }, + { + 'label': _('Stock Qty'), + 'fieldname': 'stock_qty', + 'fieldtype': 'Float', + 'width': 100 + }, + { + 'label': _('Stock UOM'), + 'fieldname': 'stock_uom', + 'fieldtype': 'Link', + 'options': 'UOM', + 'width': 100 + }, + { + 'label': _('Rate'), + 'fieldname': 'rate', + 'fieldtype': 'Float', + 'options': 'currency', + 'width': 100 + }, + { + 'label': _('Amount'), + 'fieldname': 'amount', + 'fieldtype': 'Currency', + 'options': 'currency', + 'width': 100 + } ] + if filters.get('group_by'): + columns.append({ + 'label': _('% Of Grand Total'), + 'fieldname': 'percent_gt', + 'fieldtype': 'Float', + 'width': 80 + }) + return columns def get_conditions(filters): @@ -127,6 +349,16 @@ def get_conditions(filters): where parent=`tabSales Invoice`.name and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)""" + if not filters.get("group_by"): + conditions += "ORDER BY `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_code desc" + + if filters.get("group_by"): + if filters.get("group_by") == 'Invoice': + conditions += " ORDER BY `tabSales Invoice Item`.parent desc" + elif filters.get("group_by") == 'Item': + conditions += " ORDER BY `tabSales Invoice Item`.item_code desc" + else: + conditions += " ORDER BY {0}".format(frappe.scrub(filters.get("group_by"))) return conditions @@ -157,7 +389,6 @@ def get_items(filters, additional_query_columns): from `tabSales Invoice`, `tabSales Invoice Item` where `tabSales Invoice`.name = `tabSales Invoice Item`.parent and `tabSales Invoice`.docstatus = 1 %s %s - order by `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_code desc """.format(additional_query_columns or '') % (conditions, match_conditions), filters, as_dict=1) def get_delivery_notes_against_sales_order(item_list): @@ -177,6 +408,15 @@ def get_delivery_notes_against_sales_order(item_list): return so_dn_map +def get_grand_total(filters): + conditions = get_conditions(filters) + + return frappe.db.sql(""" SELECT + SUM(`tabSales Invoice`.grand_total) + FROM `tabSales Invoice` + WHERE `tabSales Invoice`.docstatus = 1 %s + """ % (conditions), filters)[0][0] + def get_deducted_taxes(): return frappe.db.sql_list("select name from `tabPurchase Taxes and Charges` where add_deduct_tax = 'Deduct'") @@ -264,9 +504,36 @@ def get_tax_accounts(item_list, columns, company_currency, tax_columns.sort() for desc in tax_columns: - columns.append(desc + " Rate:Data:80") - columns.append(desc + " Amount:Currency/currency:100") + columns.append({ + 'label': _(desc + ' Rate'), + 'fieldname': frappe.scrub(desc + ' Rate'), + 'fieldtype': 'Float', + 'width': 100 + }) - columns += ["Total Tax:Currency/currency:80", "Total:Currency/currency:100"] + columns.append({ + 'label': _(desc + ' Amount'), + 'fieldname': frappe.scrub(desc + ' Amount'), + 'fieldtype': 'Currency', + 'options': 'currency', + 'width': 100 + }) + + columns += [ + { + 'label': _('Total Tax'), + 'fieldname': 'total_tax', + 'fieldtype': 'Currency', + 'options': 'currency', + 'width': 100 + }, + { + 'label': _('Total'), + 'fieldname': 'total', + 'fieldtype': 'Currency', + 'options': 'currency', + 'width': 100 + } + ] return itemised_tax, tax_columns diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index afdd31df16d..2be90bc5264 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -139,7 +139,7 @@ def get_columns(invoice_list, additional_table_columns): columns +=[ { - 'label': _("Custmer Group"), + 'label': _("Customer Group"), 'fieldname': 'customer_group', 'fieldtype': 'Link', 'options': 'Customer Group', @@ -175,7 +175,7 @@ def get_columns(invoice_list, additional_table_columns): 'label': _("Project"), 'fieldname': 'project', 'fieldtype': 'Link', - 'options': 'project', + 'options': 'Project', 'width': 80 }, { From 30111e640343f1bf16fafb1ec0257f9e6df773de Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 9 Jan 2020 08:41:09 +0530 Subject: [PATCH 006/929] fix: Remove group-by item --- .../item_wise_sales_register/item_wise_sales_register.js | 5 +---- .../item_wise_sales_register/item_wise_sales_register.py | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js index cff51cb8d64..1303cb93fce 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js @@ -51,16 +51,13 @@ frappe.query_reports["Item-wise Sales Register"] = { "label": __("Group By"), "fieldname": "group_by", "fieldtype": "Select", - "options": ["Customer", "Item", "Item Group", "Invoice"] + "options": ["Customer", "Item Group", "Invoice"] } ], "formatter": function(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (data && data.bold) { - // value = $(`${value}`); - // var $value = $(value).css("font-weight", "bold"); - // $value.addClass("text-danger"); value = value.bold(); } diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index af6a7b4d62c..43562976bbb 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -68,7 +68,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum prev_group_by_value = d.get(group_by_field) total_row = { - group_by_field: d.get(group_by_field), + 'item_code': d.get(group_by_field), 'stock_qty': 0.0, 'amount': 0.0, 'bold': 1, From ea5e0a9d5bfb55dd98214b0bb5e2452ad744705d Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Sat, 11 Jan 2020 16:37:46 +0530 Subject: [PATCH 007/929] fix: Grand total query fix --- .../item_wise_sales_register.js | 2 +- .../item_wise_sales_register.py | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js index 1303cb93fce..765d761fc4c 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js @@ -51,7 +51,7 @@ frappe.query_reports["Item-wise Sales Register"] = { "label": __("Group By"), "fieldname": "group_by", "fieldtype": "Select", - "options": ["Customer", "Item Group", "Invoice"] + "options": ["Customer", "Item Group", "Item", "Invoice"] } ], "formatter": function(value, row, column, data, default_formatter) { diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 43562976bbb..1c786936901 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -54,10 +54,13 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum if filters.get('group_by'): if filters.get('group_by') == 'Item': group_by_field = 'item_code' + subtotal_display_field = 'customer' elif filters.get('group_by') == 'Invoice': group_by_field = 'parent' + subtotal_display_field = 'item_code' else: group_by_field = frappe.scrub(filters.get('group_by')) + subtotal_display_field = 'item_code' if prev_group_by_value != d.get(group_by_field): if prev_group_by_value: @@ -68,7 +71,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum prev_group_by_value = d.get(group_by_field) total_row = { - 'item_code': d.get(group_by_field), + subtotal_display_field: d.get(group_by_field), 'stock_qty': 0.0, 'amount': 0.0, 'bold': 1, @@ -350,15 +353,17 @@ def get_conditions(filters): and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)""" if not filters.get("group_by"): - conditions += "ORDER BY `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_code desc" + conditions += "ORDER BY `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_group desc" if filters.get("group_by"): if filters.get("group_by") == 'Invoice': conditions += " ORDER BY `tabSales Invoice Item`.parent desc" elif filters.get("group_by") == 'Item': - conditions += " ORDER BY `tabSales Invoice Item`.item_code desc" - else: - conditions += " ORDER BY {0}".format(frappe.scrub(filters.get("group_by"))) + conditions += " ORDER BY `tabSales Invoice Item`.item_code" + elif filters.get("group_by") == 'Item Group': + conditions += " ORDER BY `tabSales Invoice Item`.`item_group`" + elif filters.get("group_by") == 'Customer': + conditions += " ORDER BY `tabSales Invoice`.customer" return conditions @@ -388,8 +393,8 @@ def get_items(filters, additional_query_columns): `tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0} from `tabSales Invoice`, `tabSales Invoice Item` where `tabSales Invoice`.name = `tabSales Invoice Item`.parent - and `tabSales Invoice`.docstatus = 1 %s %s - """.format(additional_query_columns or '') % (conditions, match_conditions), filters, as_dict=1) + and `tabSales Invoice`.docstatus = 1 {1} {2} + """.format(additional_query_columns or '', conditions, match_conditions), filters, as_dict=1, debug=1) def get_delivery_notes_against_sales_order(item_list): so_dn_map = frappe._dict() @@ -409,13 +414,13 @@ def get_delivery_notes_against_sales_order(item_list): return so_dn_map def get_grand_total(filters): - conditions = get_conditions(filters) return frappe.db.sql(""" SELECT SUM(`tabSales Invoice`.grand_total) FROM `tabSales Invoice` - WHERE `tabSales Invoice`.docstatus = 1 %s - """ % (conditions), filters)[0][0] + WHERE `tabSales Invoice`.docstatus = 1 + and posting_date between %s and %s + """, (filters.get('from_date'), filters.get('to_date')))[0][0] def get_deducted_taxes(): return frappe.db.sql_list("select name from `tabPurchase Taxes and Charges` where add_deduct_tax = 'Deduct'") From 4dfe8ab483a5be4183d7ef0251e5634a91fec53d Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Sun, 12 Jan 2020 22:14:45 +0530 Subject: [PATCH 008/929] fix: Remove debug param --- .../report/item_wise_sales_register/item_wise_sales_register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 1c786936901..65a4b24a595 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -394,7 +394,7 @@ def get_items(filters, additional_query_columns): from `tabSales Invoice`, `tabSales Invoice Item` where `tabSales Invoice`.name = `tabSales Invoice Item`.parent and `tabSales Invoice`.docstatus = 1 {1} {2} - """.format(additional_query_columns or '', conditions, match_conditions), filters, as_dict=1, debug=1) + """.format(additional_query_columns or '', conditions, match_conditions), filters, as_dict=1) #nosec def get_delivery_notes_against_sales_order(item_list): so_dn_map = frappe._dict() From c52ef56875d12e0ae0cb47f93145ef4ed811c6d0 Mon Sep 17 00:00:00 2001 From: thefalconx33 Date: Mon, 13 Jan 2020 12:43:49 +0530 Subject: [PATCH 009/929] fix: remove default customer as party type --- .../doctype/journal_entry_account/journal_entry_account.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index ab811d81b25..dbf4e5c4b41 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -90,7 +90,6 @@ "fieldtype": "Column Break" }, { - "default": "Customer", "fieldname": "party_type", "fieldtype": "Link", "in_list_view": 1, @@ -272,7 +271,7 @@ ], "idx": 1, "istable": 1, - "modified": "2019-10-02 12:23:21.693443", + "modified": "2020-01-13 12:41:33.968025", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", From 7d7027f47c40199420a084e1b862ff6b771b8b97 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 13 Jan 2020 15:09:21 +0530 Subject: [PATCH 010/929] fix: Remove patch to set automatic tax fetching from item tax template (#20235) * fix: Remove patch to set automatic tax fetching from item tax template * fix: Remove duplicate patch --- erpnext/patches.txt | 1 - .../set_default_for_add_taxes_from_item_tax_template.py | 5 ----- 2 files changed, 6 deletions(-) delete mode 100644 erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index f1dfb10729c..514305e95e2 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -645,7 +645,6 @@ erpnext.patches.v12_0.set_expense_account_in_landed_cost_voucher_taxes erpnext.patches.v12_0.replace_accounting_with_accounts_in_home_settings erpnext.patches.v12_0.set_payment_entry_status erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields -erpnext.patches.v12_0.set_default_for_add_taxes_from_item_tax_template erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger erpnext.patches.v12_0.update_price_or_product_discount erpnext.patches.v12_0.add_export_type_field_in_party_master diff --git a/erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py b/erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py deleted file mode 100644 index 06ee7981980..00000000000 --- a/erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py +++ /dev/null @@ -1,5 +0,0 @@ -import frappe - -def execute(): - frappe.db.set_value("Accounts Settings", None, "add_taxes_from_item_tax_template", 1) - frappe.db.set_default("add_taxes_from_item_tax_template", 1) \ No newline at end of file From 6e1ef4f5c2ef9eae9628775a8977cbb18aaf9508 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 14 Jan 2020 11:38:00 +0530 Subject: [PATCH 011/929] fix: gl not generated on manual asset creation (#20266) --- erpnext/assets/doctype/asset/asset.js | 51 ++++++++++++--------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index f6a7fa20d08..a53ff881777 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -338,25 +338,12 @@ frappe.ui.form.on('Asset', { }) }, - purchase_receipt: function(frm) { + purchase_receipt: (frm) => { frm.trigger('toggle_reference_doc'); - if (frm.doc.purchase_receipt) { if (frm.doc.item_code) { frappe.db.get_doc('Purchase Receipt', frm.doc.purchase_receipt).then(pr_doc => { - frm.set_value('company', pr_doc.company); - frm.set_value('purchase_date', pr_doc.posting_date); - const item = pr_doc.items.find(item => item.item_code === frm.doc.item_code); - if (!item) { - frm.set_value('purchase_receipt', ''); - frappe.msgprint({ - title: __('Invalid Purchase Receipt'), - message: __("The selected Purchase Receipt doesn't contains selected Asset Item."), - indicator: 'red' - }); - } - frm.set_value('gross_purchase_amount', item.base_net_rate); - frm.set_value('location', item.asset_location); + frm.events.set_values_from_purchase_doc(frm, 'Purchase Receipt', pr_doc) }); } else { frm.set_value('purchase_receipt', ''); @@ -368,24 +355,12 @@ frappe.ui.form.on('Asset', { } }, - purchase_invoice: function(frm) { + purchase_invoice: (frm) => { frm.trigger('toggle_reference_doc'); if (frm.doc.purchase_invoice) { if (frm.doc.item_code) { frappe.db.get_doc('Purchase Invoice', frm.doc.purchase_invoice).then(pi_doc => { - frm.set_value('company', pi_doc.company); - frm.set_value('purchase_date', pi_doc.posting_date); - const item = pi_doc.items.find(item => item.item_code === frm.doc.item_code); - if (!item) { - frm.set_value('purchase_invoice', ''); - frappe.msgprint({ - title: __('Invalid Purchase Invoice'), - message: __("The selected Purchase Invoice doesn't contains selected Asset Item."), - indicator: 'red' - }); - } - frm.set_value('gross_purchase_amount', item.base_net_rate); - frm.set_value('location', item.asset_location); + frm.events.set_values_from_purchase_doc(frm, 'Purchase Invoice', pi_doc) }); } else { frm.set_value('purchase_invoice', ''); @@ -397,6 +372,24 @@ frappe.ui.form.on('Asset', { } }, + set_values_from_purchase_doc: function(frm, doctype, purchase_doc) { + frm.set_value('company', purchase_doc.company); + frm.set_value('purchase_date', purchase_doc.posting_date); + const item = purchase_doc.items.find(item => item.item_code === frm.doc.item_code); + if (!item) { + doctype_field = frappe.scrub(doctype) + frm.set_value(doctype_field, ''); + frappe.msgprint({ + title: __(`Invalid ${doctype}`), + message: __(`The selected ${doctype} doesn't contains selected Asset Item.`), + indicator: 'red' + }); + } + frm.set_value('gross_purchase_amount', item.base_net_rate + item.item_tax_amount); + frm.set_value('purchase_receipt_amount', item.base_net_rate + item.item_tax_amount); + frm.set_value('location', item.asset_location); + }, + set_depreciation_rate: function(frm, row) { if (row.total_number_of_depreciations && row.frequency_of_depreciation && row.expected_value_after_useful_life) { From eb7893768bdc9daf38571f915e3c79ccf984620f Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 14 Jan 2020 12:07:30 +0530 Subject: [PATCH 012/929] fix: payment entry can be created against on hold PI (#20271) --- .../doctype/purchase_invoice/purchase_invoice.js | 15 +++++++++++++-- .../doctype/purchase_invoice/purchase_invoice.py | 7 ++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 643de7d300a..c5c54837a7b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -167,8 +167,15 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ make_comment_dialog_and_block_invoice: function(){ const me = this; - const title = __('Add Comment'); + const title = __('Block Invoice'); const fields = [ + { + fieldname: 'release_date', + read_only: 0, + fieldtype:'Date', + label: __('Release Date'), + default: me.frm.doc.release_date + }, { fieldname: 'hold_comment', read_only: 0, @@ -187,7 +194,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ const dialog_data = me.dialog.get_values(); frappe.call({ 'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.block_invoice', - 'args': {'name': me.frm.doc.name, 'hold_comment': dialog_data.hold_comment}, + 'args': { + 'name': me.frm.doc.name, + 'hold_comment': dialog_data.hold_comment, + 'release_date': dialog_data.release_date + }, 'callback': (r) => me.frm.reload_doc() }); me.dialog.hide(); diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 917acba92c9..1a14a2a732d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -927,9 +927,10 @@ class PurchaseInvoice(BuyingController): def on_recurring(self, reference_doc, auto_repeat_doc): self.due_date = None - def block_invoice(self, hold_comment=None): + def block_invoice(self, hold_comment=None, release_date=None): self.db_set('on_hold', 1) self.db_set('hold_comment', cstr(hold_comment)) + self.db_set('release_date', release_date) def unblock_invoice(self): self.db_set('on_hold', 0) @@ -1013,10 +1014,10 @@ def unblock_invoice(name): @frappe.whitelist() -def block_invoice(name, hold_comment): +def block_invoice(name, hold_comment, release_date): if frappe.db.exists('Purchase Invoice', name): pi = frappe.get_doc('Purchase Invoice', name) - pi.block_invoice(hold_comment) + pi.block_invoice(hold_comment, release_date) @frappe.whitelist() def make_inter_company_sales_invoice(source_name, target_doc=None): From e3b6a14ab1bd0b6b94555afbc0d4320260548f02 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 14 Jan 2020 12:12:54 +0530 Subject: [PATCH 013/929] fix: filter issue for the stock and account value comparision report --- .../stock_and_account_value_comparison.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py index eef121e542f..94ec314e8c9 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py @@ -43,7 +43,7 @@ def get_data(report_filters): def get_stock_ledger_data(report_filters, filters): if report_filters.account: warehouses = get_warehouses_based_on_account(report_filters.account, - report_filters.warehouse) + report_filters.company) filters["warehouse"] = ("in", warehouses) From 6e115638a13cd278ddcd3ac5b5853fb1bab999f5 Mon Sep 17 00:00:00 2001 From: Pranav Nachnekar Date: Wed, 15 Jan 2020 09:19:28 +0000 Subject: [PATCH 014/929] fix: remove `debugger` statement (#20295) backport of https://github.com/frappe/erpnext/pull/20294 --- erpnext/selling/doctype/sales_order/sales_order.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 45dd51891c9..fa765dfaad9 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -135,8 +135,6 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( } if(doc.status !== 'Closed') { if(doc.status !== 'On Hold') { - debugger; - allow_delivery = this.frm.doc.items.some(item => item.delivered_by_supplier === 0 && item.qty > flt(item.delivered_qty)) && !this.frm.doc.skip_delivery_note From fed9e2861cdc305136ecb63ffd24b269335f4507 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 15 Jan 2020 16:35:02 +0530 Subject: [PATCH 015/929] fix: incorrect number of GL Entries error in stock entry (#20297) --- erpnext/controllers/stock_controller.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index fc2128f2a42..aa3ce9d4d04 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -67,6 +67,7 @@ class StockController(AccountsController): gl_list = [] warehouse_with_no_account = [] + precision = frappe.get_precision("GL Entry", "debit_in_account_currency") for item_row in voucher_details: sle_list = sle_map.get(item_row.name) if sle_list: @@ -92,7 +93,7 @@ class StockController(AccountsController): "against": item_row.expense_account, "cost_center": item_row.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", - "debit": flt(sle.stock_value_difference, 2), + "debit": flt(sle.stock_value_difference, precision), "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No", }, warehouse_account[sle.warehouse]["account_currency"], item=item_row)) @@ -102,7 +103,7 @@ class StockController(AccountsController): "against": warehouse_account[sle.warehouse]["account"], "cost_center": item_row.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", - "credit": flt(sle.stock_value_difference, 2), + "credit": flt(sle.stock_value_difference, precision), "project": item_row.get("project") or self.get("project"), "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No" }, item=item_row)) From 8c45ae73b3a44de6be3e6b21b36420bff5afde79 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 15 Jan 2020 16:36:19 +0530 Subject: [PATCH 016/929] fix: get existing payment req amount only from unpaid req (#20290) --- erpnext/accounts/doctype/payment_request/payment_request.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 6133b1ccd4b..1aff43c7177 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -373,6 +373,7 @@ def get_existing_payment_request_amount(ref_dt, ref_dn): reference_doctype = %s and reference_name = %s and docstatus = 1 + and status != 'Paid' """, (ref_dt, ref_dn)) return flt(existing_payment_request_amount[0][0]) if existing_payment_request_amount else 0 From 8dfd5535f171944b07405a46884fb40917f78c9f Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Wed, 15 Jan 2020 18:57:20 +0530 Subject: [PATCH 017/929] fix(patch): do not append taxes to template if account name is not set (#20311) --- .../move_item_tax_to_item_tax_template.py | 60 ++++++++++--------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py index 5a127950006..31d500ea2f7 100644 --- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py +++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py @@ -49,12 +49,13 @@ def execute(): item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code) # update the item tax table - item = frappe.get_doc("Item", item_code) - item.set("taxes", []) - item.append("taxes", {"item_tax_template": item_tax_template_name, "tax_category": ""}) frappe.db.sql("delete from `tabItem Tax` where parent=%s and parenttype='Item'", item_code) - for d in item.taxes: - d.db_insert() + if item_tax_template_name: + item = frappe.get_doc("Item", item_code) + item.set("taxes", []) + item.append("taxes", {"item_tax_template": item_tax_template_name, "tax_category": ""}) + for d in item.taxes: + d.db_insert() doctypes = [ 'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice', @@ -95,30 +96,35 @@ def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttyp else: parts = tax_type.strip().split(" - ") account_name = " - ".join(parts[:-1]) - company = get_company(parts[-1], parenttype, parent) - parent_account = frappe.db.get_value("Account", - filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account") - if not parent_account: + if not account_name: + tax_type = None + else: + company = get_company(parts[-1], parenttype, parent) parent_account = frappe.db.get_value("Account", - filters={"account_type": "Tax", "root_type": "Liability", "is_group": 1, "company": company}) - filters = { - "account_name": account_name, - "company": company, - "account_type": "Tax", - "parent_account": parent_account - } - tax_type = frappe.db.get_value("Account", filters) - if not tax_type: - account = frappe.new_doc("Account") - account.update(filters) - account.insert() - tax_type = account.name + filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account") + if not parent_account: + parent_account = frappe.db.get_value("Account", + filters={"account_type": "Tax", "root_type": "Liability", "is_group": 1, "company": company}) + filters = { + "account_name": account_name, + "company": company, + "account_type": "Tax", + "parent_account": parent_account + } + tax_type = frappe.db.get_value("Account", filters) + if not tax_type: + account = frappe.new_doc("Account") + account.update(filters) + account.insert() + tax_type = account.name - item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate}) - item_tax_templates.setdefault(item_tax_template.title, {}) - item_tax_templates[item_tax_template.title][tax_type] = tax_rate - item_tax_template.save() - return item_tax_template.name + if tax_type: + item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate}) + item_tax_templates.setdefault(item_tax_template.title, {}) + item_tax_templates[item_tax_template.title][tax_type] = tax_rate + if item_tax_template.get("taxes"): + item_tax_template.save() + return item_tax_template.name def get_company(company_abbr, parenttype=None, parent=None): if parenttype and parent: From 89affd8d09863a18011313b33927b21f54fe231b Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Wed, 15 Jan 2020 18:58:09 +0530 Subject: [PATCH 018/929] fix(patch): reload tax category (#20307) --- .../doctype/tax_category/tax_category.json | 240 +++++++++--------- .../patches/v8_7/sync_india_custom_fields.py | 4 +- 2 files changed, 123 insertions(+), 121 deletions(-) diff --git a/erpnext/accounts/doctype/tax_category/tax_category.json b/erpnext/accounts/doctype/tax_category/tax_category.json index 3556e127675..1e3ae455b37 100644 --- a/erpnext/accounts/doctype/tax_category/tax_category.json +++ b/erpnext/accounts/doctype/tax_category/tax_category.json @@ -1,134 +1,134 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:title", - "beta": 0, - "creation": "2018-11-22 23:38:39.668804", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:title", + "beta": 0, + "creation": "2018-11-22 23:38:39.668804", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "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": 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": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-22 23:38:39.668804", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Tax Category", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2020-01-15 17:14:28.951793", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Tax Category", + "name_case": "", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "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": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "amend": 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": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 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": "Accounts User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 0 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/patches/v8_7/sync_india_custom_fields.py b/erpnext/patches/v8_7/sync_india_custom_fields.py index 80d66c2ab19..73e1b182e82 100644 --- a/erpnext/patches/v8_7/sync_india_custom_fields.py +++ b/erpnext/patches/v8_7/sync_india_custom_fields.py @@ -13,6 +13,8 @@ def execute(): frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration_category') frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission_detail') + frappe.reload_doc('accounts', 'doctype', 'tax_category') + for doctype in ["Sales Invoice", "Delivery Note", "Purchase Invoice"]: frappe.db.sql("""delete from `tabCustom Field` where dt = %s and fieldname in ('port_code', 'shipping_bill_number', 'shipping_bill_date')""", doctype) @@ -29,4 +31,4 @@ def execute(): update tabAddress set gst_state_number=concat("0", gst_state_number) where ifnull(gst_state_number, '') != '' and gst_state_number<10 - """) \ No newline at end of file + """) From 611e23c149cdba96f8848e8813036de49b9eae55 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 15 Jan 2020 19:05:42 +0530 Subject: [PATCH 019/929] fix: make jv depreciations only if calculate depr is checked (#20283) --- erpnext/assets/doctype/asset/depreciation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 2d23f77014e..ad671ba0f2c 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -22,7 +22,7 @@ def post_depreciation_entries(date=None): def get_depreciable_assets(date): return frappe.db.sql_list("""select a.name from tabAsset a, `tabDepreciation Schedule` ds - where a.name = ds.parent and a.docstatus=1 and ds.schedule_date<=%s + where a.name = ds.parent and a.docstatus=1 and ds.schedule_date<=%s and a.calculate_depreciation = 1 and a.status in ('Submitted', 'Partially Depreciated') and ifnull(ds.journal_entry, '')=''""", date) From 6ba6ba27bd991695b60589271a124a66d4b60b48 Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 15 Jan 2020 19:22:21 +0530 Subject: [PATCH 020/929] fix: Missing argument in status updater (#20284) --- erpnext/controllers/status_updater.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 2b2c27b052f..8b275a64fb3 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -182,7 +182,7 @@ class StatusUpdater(Document): if args.get('no_allowance'): item['reduce_by'] = item[args['target_field']] - item[args['target_ref_field']] if item['reduce_by'] > .01: - self.limits_crossed_error(args, item) + self.limits_crossed_error(args, item, "qty") elif item[args['target_ref_field']]: self.check_overflow_with_allowance(item, args) From 0a50088a0e8a5d7634de8416a93c59112f83bced Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 15 Jan 2020 19:24:10 +0530 Subject: [PATCH 021/929] fix: Applied query on PO Supplier popup field in Material Request. (#20232) --- .../doctype/material_request/material_request.js | 15 +++++++++++++-- .../doctype/material_request/material_request.py | 12 ++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 935d61310eb..7ef2d6460f6 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -230,8 +230,19 @@ frappe.ui.form.on('Material Request', { make_purchase_order: function(frm) { frappe.prompt( - {fieldname:'default_supplier', label: __('For Default Supplier (optional)'), description: __('Selected Supplier\ - must be the Default Supplier of one of the items below.'), fieldtype: 'Link', options: 'Supplier'}, + { + label: __('For Default Supplier (Optional)'), + fieldname:'default_supplier', + fieldtype: 'Link', + options: 'Supplier', + description: __('Select a Supplier from the Default Supplier List of the items below.'), + get_query: () => { + return{ + query: "erpnext.stock.doctype.material_request.material_request.get_default_supplier_query", + filters: {'doc': frm.doc.name} + } + } + }, (values) => { frappe.model.open_mapped_doc({ method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order", diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 44e890cc50e..6531e095b84 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -386,6 +386,18 @@ def get_material_requests_based_on_supplier(supplier): return material_requests, supplier_items +def get_default_supplier_query(doctype, txt, searchfield, start, page_len, filters): + doc = frappe.get_doc("Material Request", filters.get("doc")) + item_list = [] + for d in doc.items: + item_list.append(d.item_code) + + return frappe.db.sql("""select default_supplier + from `tabItem Default` + where parent in ({0}) and + default_supplier IS NOT NULL + """.format(', '.join(['%s']*len(item_list))),tuple(item_list)) + @frappe.whitelist() def make_supplier_quotation(source_name, target_doc=None): def postprocess(source, target_doc): From 7721954bbb01c84b285350d905616558d19d4830 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 16 Jan 2020 13:37:20 +0530 Subject: [PATCH 022/929] fix: Multiple fixes based on testing on pre-release branch (#20303) * fix: Multiple fixes based on testing on pre-release branch * fix: reload hr settings --- .../purchase_order/purchase_order.json | 6 +- .../employee_advance/employee_advance.js | 8 +- .../employee_advance/employee_advance.py | 7 +- .../hr/doctype/expense_claim/expense_claim.js | 12 +- .../hr/doctype/expense_claim/expense_claim.py | 1 + .../expense_claim_advance.json | 305 +++++------------- erpnext/manufacturing/doctype/bom/bom.py | 44 ++- .../doctype/work_order/work_order.py | 5 - erpnext/patches.txt | 2 +- .../stock/doctype/stock_entry/stock_entry.py | 40 +-- erpnext/stock/get_item_details.py | 9 +- 11 files changed, 148 insertions(+), 291 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 9201eef9b49..d82e128735e 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1,5 +1,4 @@ { - "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", @@ -48,7 +47,6 @@ "ignore_pricing_rule", "sec_warehouse", "set_warehouse", - "set_reserve_warehouse", "col_break_warehouse", "is_subcontracted", "supplier_warehouse", @@ -58,6 +56,7 @@ "section_break_48", "pricing_rules", "raw_material_details", + "set_reserve_warehouse", "supplied_items", "sb_last_purchase", "total_qty", @@ -1054,8 +1053,7 @@ "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, - "links": [], - "modified": "2019-12-30 19:11:54.122264", + "modified": "2020-01-14 18:54:39.694448", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js index ba62853336d..389660387b7 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.js +++ b/erpnext/hr/doctype/employee_advance/employee_advance.js @@ -47,7 +47,7 @@ frappe.ui.form.on('Employee Advance', { } if (frm.doc.docstatus === 1 - && (flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount)) + && (flt(frm.doc.claimed_amount) + flt(frm.doc.return_amount) < flt(frm.doc.paid_amount)) && frappe.model.can_create("Journal Entry")) { frm.add_custom_button(__("Return"), function() { @@ -96,12 +96,12 @@ frappe.ui.form.on('Employee Advance', { frappe.call({ method: 'erpnext.hr.doctype.employee_advance.employee_advance.make_return_entry', args: { - 'employee_name': frm.doc.employee, + 'employee': frm.doc.employee, 'company': frm.doc.company, 'employee_advance_name': frm.doc.name, 'return_amount': flt(frm.doc.paid_amount - frm.doc.claimed_amount), - 'mode_of_payment': frm.doc.mode_of_payment, - 'advance_account': frm.doc.advance_account + 'advance_account': frm.doc.advance_account, + 'mode_of_payment': frm.doc.mode_of_payment }, callback: function(r) { const doclist = frappe.model.sync(r.message); diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index 7fe2ebc79e0..f10e3b6ce26 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -133,7 +133,8 @@ def make_bank_entry(dt, dn): return je.as_dict() @frappe.whitelist() -def make_return_entry(employee_name, company, employee_advance_name, return_amount, mode_of_payment, advance_account): +def make_return_entry(employee, company, employee_advance_name, + return_amount, advance_account, mode_of_payment=None): return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment) je = frappe.new_doc('Journal Entry') je.posting_date = nowdate() @@ -147,7 +148,7 @@ def make_return_entry(employee_name, company, employee_advance_name, return_amou 'reference_type': 'Employee Advance', 'reference_name': employee_advance_name, 'party_type': 'Employee', - 'party': employee_name, + 'party': employee, 'is_advance': 'Yes' }) @@ -159,5 +160,5 @@ def make_return_entry(employee_name, company, employee_advance_name, return_amou }) return je.as_dict() - + diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 0d37c10e9cc..3194007f231 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -213,13 +213,14 @@ frappe.ui.form.on("Expense Claim", { }, update_employee_advance_claimed_amount: function(frm) { + console.log("update_employee_advance_claimed_amount") let amount_to_be_allocated = frm.doc.grand_total; $.each(frm.doc.advances || [], function(i, advance){ if (amount_to_be_allocated >= advance.unclaimed_amount){ - frm.doc.advances[i].allocated_amount = frm.doc.advances[i].unclaimed_amount; + advance.allocated_amount = frm.doc.advances[i].unclaimed_amount; amount_to_be_allocated -= advance.allocated_amount; } else{ - frm.doc.advances[i].allocated_amount = amount_to_be_allocated; + advance.allocated_amount = amount_to_be_allocated; amount_to_be_allocated = 0; } frm.refresh_field("advances"); @@ -295,6 +296,7 @@ frappe.ui.form.on("Expense Claim", { doc: frm.doc, callback: () => { refresh_field("taxes"); + frm.trigger("update_employee_advance_claimed_amount"); } }); } @@ -331,16 +333,12 @@ frappe.ui.form.on("Expense Claim", { frappe.ui.form.on("Expense Claim Detail", { amount: function(frm, cdt, cdn) { var child = locals[cdt][cdn]; - var doc = frm.doc; frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.amount); - cur_frm.cscript.calculate_total(doc,cdt,cdn); }, sanctioned_amount: function(frm, cdt, cdn) { - var doc = frm.doc; - cur_frm.cscript.calculate_total(doc,cdt,cdn); + cur_frm.cscript.calculate_total(frm.doc, cdt, cdn); frm.trigger("get_taxes"); - frm.trigger("calculate_grand_total"); } }); diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index 4519fdc20b2..d29b3f75879 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -243,6 +243,7 @@ class ExpenseClaim(AccountsController): precision = self.precision("total_advance_amount") if flt(self.total_advance_amount, precision) > flt(self.total_claimed_amount, precision): frappe.throw(_("Total advance amount cannot be greater than total claimed amount")) + if self.total_sanctioned_amount \ and flt(self.total_advance_amount, precision) > flt(self.total_sanctioned_amount, precision): frappe.throw(_("Total advance amount cannot be greater than total sanctioned amount")) diff --git a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json index b5e4fd16f8e..64eb3095b3d 100644 --- a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json +++ b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json @@ -1,238 +1,93 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-10-09 16:53:26.410762", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "creation": "2017-10-09 16:53:26.410762", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee_advance", + "posting_date", + "advance_paid", + "unclaimed_amount", + "allocated_amount", + "advance_account" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "employee_advance", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee Advance", - "length": 0, - "no_copy": 1, - "oldfieldname": "journal_voucher", - "oldfieldtype": "Link", - "options": "Employee Advance", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "250px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "columns": 2, + "fieldname": "employee_advance", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee Advance", + "no_copy": 1, + "oldfieldname": "journal_voucher", + "oldfieldtype": "Link", + "options": "Employee Advance", + "print_width": "250px", + "reqd": 1, "width": "250px" - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Posting Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "advance_paid", - "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": "Advance Paid", - "length": 0, - "no_copy": 0, - "options": "Company:company.default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "advance_paid", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Advance Paid", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "unclaimed_amount", - "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": "Unclaimed amount", - "length": 0, - "no_copy": 1, - "oldfieldname": "advance_amount", - "oldfieldtype": "Currency", - "options": "Company:company.default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "120px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "columns": 2, + "fieldname": "unclaimed_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Unclaimed amount", + "no_copy": 1, + "oldfieldname": "advance_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_width": "120px", + "read_only": 1, + "reqd": 1, "width": "120px" - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "allocated_amount", - "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": "Allocated amount", - "length": 0, - "no_copy": 1, - "oldfieldname": "allocated_amount", - "oldfieldtype": "Currency", - "options": "Company:company.default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "120px", - "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": "allocated_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Allocated amount", + "no_copy": 1, + "oldfieldname": "allocated_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_width": "120px", "width": "120px" - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "advance_account", - "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": "Advance Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "advance_account", + "fieldtype": "Link", + "hidden": 1, + "label": "Advance Account", + "options": "Account" } - ], - "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": "2017-10-09 19:59:48.818139", - "modified_by": "Administrator", - "module": "HR", - "name": "Expense Claim Advance", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "istable": 1, + "modified": "2020-01-14 17:49:17.968373", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claim Advance", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 686b1967eb5..98f10059712 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -633,7 +633,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite is_stock_item=is_stock_item, qty_field="stock_qty", select_columns = """, bom_item.source_warehouse, bom_item.operation, - bom_item.include_item_in_manufacturing, bom_item.description, + bom_item.include_item_in_manufacturing, bom_item.description, bom_item.rate, (Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s limit 1) as idx""") items = frappe.db.sql(query, { "parent": bom, "qty": qty, "bom": bom, "company": company }, as_dict=True) @@ -647,7 +647,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite qty_field="stock_qty" if fetch_qty_in_stock_uom else "qty", select_columns = """, bom_item.uom, bom_item.conversion_factor, bom_item.source_warehouse, bom_item.idx, bom_item.operation, bom_item.include_item_in_manufacturing, - bom_item.description """) + bom_item.description, bom_item.base_rate as rate """) items = frappe.db.sql(query, { "qty": qty, "bom": bom, "company": company }, as_dict=True) for item in items: @@ -760,11 +760,17 @@ def get_boms_in_bottom_up_order(bom_no=None): def add_additional_cost(stock_entry, work_order): # Add non stock items cost in the additional cost - bom = frappe.get_doc('BOM', work_order.bom_no) - table = 'exploded_items' if work_order.get('use_multi_level_bom') else 'items' + stock_entry.additional_costs = [] expenses_included_in_valuation = frappe.get_cached_value("Company", work_order.company, "expenses_included_in_valuation") + add_non_stock_items_cost(stock_entry, work_order, expenses_included_in_valuation) + add_operations_cost(stock_entry, work_order, expenses_included_in_valuation) + +def add_non_stock_items_cost(stock_entry, work_order, expense_account): + bom = frappe.get_doc('BOM', work_order.bom_no) + table = 'exploded_items' if work_order.get('use_multi_level_bom') else 'items' + items = {} for d in bom.get(table): items.setdefault(d.item_code, d.amount) @@ -772,11 +778,35 @@ def add_additional_cost(stock_entry, work_order): non_stock_items = frappe.get_all('Item', fields="name", filters={'name': ('in', list(items.keys())), 'ifnull(is_stock_item, 0)': 0}, as_list=1) + non_stock_items_cost = 0.0 for name in non_stock_items: + non_stock_items_cost += flt(items.get(name[0])) * flt(stock_entry.fg_completed_qty) / flt(bom.quantity) + + stock_entry.append('additional_costs', { + 'expense_account': expense_account, + 'description': _("Non stock items"), + 'amount': non_stock_items_cost + }) + +def add_operations_cost(stock_entry, work_order=None, expense_account=None): + from erpnext.stock.doctype.stock_entry.stock_entry import get_operating_cost_per_unit + operating_cost_per_unit = get_operating_cost_per_unit(work_order, stock_entry.bom_no) + + if operating_cost_per_unit: stock_entry.append('additional_costs', { - 'expense_account': expenses_included_in_valuation, - 'description': name[0], - 'amount': flt(items.get(name[0])) * flt(stock_entry.fg_completed_qty) / flt(bom.quantity) + "expense_account": expense_account, + "description": _("Operating Cost as per Work Order / BOM"), + "amount": operating_cost_per_unit * flt(stock_entry.fg_completed_qty) + }) + + if work_order and work_order.additional_operating_cost and work_order.qty: + additional_operating_cost_per_unit = \ + flt(work_order.additional_operating_cost) / flt(work_order.qty) + + stock_entry.append('additional_costs', { + "expense_account": expense_account, + "description": "Additional Operating Cost", + "amount": additional_operating_cost_per_unit * flt(stock_entry.fg_completed_qty) }) @frappe.whitelist() diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 1cb69012083..b4dc3a55699 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -13,7 +13,6 @@ from dateutil.relativedelta import relativedelta from erpnext.stock.doctype.item.item import validate_end_of_life from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError from erpnext.projects.doctype.timesheet.timesheet import OverlapError -from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty from frappe.utils.csvutils import getlink @@ -671,10 +670,6 @@ def make_stock_entry(work_order_id, purpose, qty=None): stock_entry.from_warehouse = wip_warehouse stock_entry.to_warehouse = work_order.fg_warehouse stock_entry.project = work_order.project - if purpose=="Manufacture": - additional_costs = get_additional_costs(work_order, fg_qty=stock_entry.fg_completed_qty, - company=work_order.company) - stock_entry.set("additional_costs", additional_costs) stock_entry.set_stock_entry_type() stock_entry.get_items() diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 514305e95e2..17c9a20766d 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -13,6 +13,7 @@ erpnext.patches.v4_0.apply_user_permissions erpnext.patches.v4_0.move_warehouse_user_to_restrictions erpnext.patches.v4_0.global_defaults_to_system_settings erpnext.patches.v4_0.update_incharge_name_to_sales_person_in_maintenance_schedule +execute:frappe.reload_doc("HR", "doctype", "HR Settings") #2020-01-16 execute:frappe.reload_doc('stock', 'doctype', 'warehouse') # 2017-04-24 execute:frappe.reload_doc('accounts', 'doctype', 'sales_invoice') # 2016-08-31 execute:frappe.reload_doc('selling', 'doctype', 'sales_order') # 2014-01-29 @@ -513,7 +514,6 @@ erpnext.patches.v11_0.rename_employee_loan_to_loan erpnext.patches.v11_0.move_leave_approvers_from_employee #13-06-2018 erpnext.patches.v11_0.update_department_lft_rgt erpnext.patches.v11_0.add_default_email_template_for_leave -execute:frappe.reload_doc("HR", "doctype", "HR Settings") erpnext.patches.v11_0.set_default_email_template_in_hr #08-06-2018 erpnext.patches.v11_0.uom_conversion_data #30-06-2018 erpnext.patches.v10_0.taxes_issue_with_pos diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 8f678837773..996727042e3 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -478,15 +478,17 @@ class StockEntry(StockController): def set_basic_rate_for_finished_goods(self, raw_material_cost, scrap_material_cost): if self.purpose in ["Manufacture", "Repack"]: for d in self.get("items"): - if (d.transfer_qty and (d.bom_no or d.t_warehouse) and raw_material_cost + if (d.transfer_qty and (d.bom_no or d.t_warehouse) and (getattr(self, "pro_doc", frappe._dict()).scrap_warehouse != d.t_warehouse)): - d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate")) - d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount")) - if (not d.basic_rate and self.work_order and - frappe.db.get_single_value("Manufacturing Settings", "material_consumption")): - d.basic_rate = get_valuation_rate_for_finished_good_entry(self.work_order) or 0 - d.basic_amount = d.basic_rate * d.qty + if self.work_order \ + and frappe.db.get_single_value("Manufacturing Settings", "material_consumption"): + bom_items = self.get_bom_raw_materials(d.transfer_qty) + raw_material_cost = sum([flt(d.qty)*flt(d.rate) for d in bom_items.values()]) + + if raw_material_cost: + d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate")) + d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount")) def distribute_additional_costs(self): if self.purpose == "Material Issue": @@ -1402,30 +1404,6 @@ def get_work_order_details(work_order, company): "additional_costs": get_additional_costs(work_order, fg_qty=pending_qty_to_produce, company=company) } -def get_additional_costs(work_order=None, bom_no=None, fg_qty=None, company=None): - additional_costs = [] - operating_cost_per_unit = get_operating_cost_per_unit(work_order, bom_no) - expenses_included_in_valuation = frappe.get_cached_value("Company", company, "expenses_included_in_valuation") - - if operating_cost_per_unit: - additional_costs.append({ - "expense_account": expenses_included_in_valuation, - "description": "Operating Cost as per Work Order / BOM", - "amount": operating_cost_per_unit * flt(fg_qty) - }) - - if work_order and work_order.additional_operating_cost and work_order.qty: - additional_operating_cost_per_unit = \ - flt(work_order.additional_operating_cost) / flt(work_order.qty) - - additional_costs.append({ - "expense_account": expenses_included_in_valuation, - "description": "Additional Operating Cost", - "amount": additional_operating_cost_per_unit * flt(fg_qty) - }) - - return additional_costs - def get_operating_cost_per_unit(work_order=None, bom_no=None): operating_cost_per_unit = 0 if work_order: diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 6fc6990df76..dec3801feaa 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -608,7 +608,7 @@ def get_item_price(args, item_code, ignore_party=False): return frappe.db.sql(""" select name, price_list_rate, uom from `tabItem Price` {conditions} - order by uom desc, min_qty desc, valid_from desc """.format(conditions=conditions), args) + order by valid_from desc, min_qty desc, uom desc """.format(conditions=conditions), args) def get_price_list_rate_for(args, item_code): """ @@ -630,7 +630,8 @@ def get_price_list_rate_for(args, item_code): "customer": args.get('customer'), "supplier": args.get('supplier'), "uom": args.get('uom'), - "min_qty": args.get('qty'), + "min_qty": args.get('qty') if args.get('price_list_uom_dependant')\ + else flt(args.get('qty')) * flt(args.get("conversion_factor", 1)), "transaction_date": args.get('transaction_date'), } @@ -644,8 +645,8 @@ def get_price_list_rate_for(args, item_code): for field in ["customer", "supplier"]: del item_price_args[field] - general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party")) - + general_price_list_rate = get_item_price(item_price_args, item_code, + ignore_party=args.get("ignore_party")) if not general_price_list_rate: del item_price_args["min_qty"] general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party")) From 514d3c37b98b532dec473f4ab5927a71dc7f1325 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 16 Jan 2020 13:38:21 +0530 Subject: [PATCH 023/929] fix: hide total amount field from journal entry PF (#20325) --- .../doctype/journal_entry/journal_entry.json | 2193 ++++------------- 1 file changed, 477 insertions(+), 1716 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index 2f9f215fe86..f5991241a72 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -1,1780 +1,541 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2013-03-25 10:53:52", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-03-25 10:53:52", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "entry_type_and_date", + "title", + "voucher_type", + "naming_series", + "column_break1", + "posting_date", + "company", + "finance_book", + "2_add_edit_gl_entries", + "accounts", + "section_break99", + "cheque_no", + "cheque_date", + "user_remark", + "column_break99", + "total_debit", + "total_credit", + "difference", + "get_balance", + "multi_currency", + "total_amount_currency", + "total_amount", + "total_amount_in_words", + "reference", + "clearance_date", + "remark", + "paid_loan", + "inter_company_journal_entry_reference", + "column_break98", + "bill_no", + "bill_date", + "due_date", + "write_off", + "write_off_based_on", + "get_outstanding_invoices", + "column_break_30", + "write_off_amount", + "printing_settings", + "pay_to_recd_from", + "column_break_35", + "letter_head", + "select_print_heading", + "addtional_info", + "mode_of_payment", + "payment_order", + "column_break3", + "is_opening", + "stock_entry", + "subscription_section", + "auto_repeat", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "entry_type_and_date", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "options": "fa fa-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 - }, + "fieldname": "entry_type_and_date", + "fieldtype": "Section Break", + "options": "fa fa-flag" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "title", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1, + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Journal Entry", - "fieldname": "voucher_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": 1, - "label": "Entry Type", - "length": 0, - "no_copy": 0, - "oldfieldname": "voucher_type", - "oldfieldtype": "Select", - "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation", - "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": "Journal Entry", + "fieldname": "voucher_type", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Entry Type", + "oldfieldname": "voucher_type", + "oldfieldtype": "Select", + "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 1, - "oldfieldname": "naming_series", - "oldfieldtype": "Select", - "options": "ACC-JV-.YYYY.-", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "oldfieldname": "naming_series", + "oldfieldtype": "Select", + "options": "ACC-JV-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "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": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Posting Date", - "length": 0, - "no_copy": 1, - "oldfieldname": "posting_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 - }, + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "no_copy": 1, + "oldfieldname": "posting_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": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "print_hide": 1, + "remember_last_selected_value": 1, + "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": "finance_book", - "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": "Finance Book", - "length": 0, - "no_copy": 0, - "options": "Finance Book", - "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": "finance_book", + "fieldtype": "Link", + "label": "Finance Book", + "options": "Finance Book" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "2_add_edit_gl_entries", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-table", - "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": "2_add_edit_gl_entries", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "fa fa-table" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "accounts", - "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": "Accounting Entries", - "length": 0, - "no_copy": 0, - "oldfieldname": "entries", - "oldfieldtype": "Table", - "options": "Journal Entry Account", - "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": "accounts", + "fieldtype": "Table", + "label": "Accounting Entries", + "oldfieldname": "entries", + "oldfieldtype": "Table", + "options": "Journal Entry Account", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break99", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break99", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "cheque_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reference Number", - "length": 0, - "no_copy": 1, - "oldfieldname": "cheque_no", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "cheque_no", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Reference Number", + "no_copy": 1, + "oldfieldname": "cheque_no", + "oldfieldtype": "Data", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cheque_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": "Reference Date", - "length": 0, - "no_copy": 1, - "oldfieldname": "cheque_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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "cheque_date", + "fieldtype": "Date", + "label": "Reference Date", + "no_copy": 1, + "oldfieldname": "cheque_date", + "oldfieldtype": "Date", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "user_remark", - "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": "User Remark", - "length": 0, - "no_copy": 1, - "oldfieldname": "user_remark", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "user_remark", + "fieldtype": "Small Text", + "label": "User Remark", + "no_copy": 1, + "oldfieldname": "user_remark", + "oldfieldtype": "Small Text", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break99", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break99", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_debit", - "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 Debit", - "length": 0, - "no_copy": 1, - "oldfieldname": "total_debit", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_debit", + "fieldtype": "Currency", + "label": "Total Debit", + "no_copy": 1, + "oldfieldname": "total_debit", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_credit", - "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 Credit", - "length": 0, - "no_copy": 1, - "oldfieldname": "total_credit", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_credit", + "fieldtype": "Currency", + "label": "Total Credit", + "no_copy": 1, + "oldfieldname": "total_credit", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "difference", - "fieldname": "difference", - "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": "Difference (Dr - Cr)", - "length": 0, - "no_copy": 1, - "oldfieldname": "difference", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "difference", + "fieldname": "difference", + "fieldtype": "Currency", + "label": "Difference (Dr - Cr)", + "no_copy": 1, + "oldfieldname": "difference", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "difference", - "fieldname": "get_balance", - "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 Difference Entry", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Button", - "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 - }, + "depends_on": "difference", + "fieldname": "get_balance", + "fieldtype": "Button", + "label": "Make Difference Entry", + "oldfieldtype": "Button" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "multi_currency", - "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": "Multi Currency", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "multi_currency", + "fieldtype": "Check", + "label": "Multi Currency", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_amount_currency", - "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": "Total Amount Currency", - "length": 0, - "no_copy": 1, - "options": "Currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_amount_currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Total Amount Currency", + "no_copy": 1, + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fieldname": "total_amount", - "fieldtype": "Currency", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Total Amount", - "length": 0, - "no_copy": 1, - "options": "total_amount_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "fieldname": "total_amount", + "fieldtype": "Currency", + "hidden": 1, + "in_list_view": 1, + "label": "Total Amount", + "no_copy": 1, + "options": "total_amount_currency", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_amount_in_words", - "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": "Total Amount in Words", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_amount_in_words", + "fieldtype": "Data", + "hidden": 1, + "label": "Total Amount in Words", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "reference", - "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": "Reference", - "length": 0, - "no_copy": 0, - "options": "fa fa-pushpin", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "reference", + "fieldtype": "Section Break", + "label": "Reference", + "options": "fa fa-pushpin" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "clearance_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": "Clearance Date", - "length": 0, - "no_copy": 1, - "oldfieldname": "clearance_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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "clearance_date", + "fieldtype": "Date", + "label": "Clearance Date", + "no_copy": 1, + "oldfieldname": "clearance_date", + "oldfieldtype": "Date", + "read_only": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "remark", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Remark", - "length": 0, - "no_copy": 1, - "oldfieldname": "remark", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "remark", + "fieldtype": "Small Text", + "in_global_search": 1, + "label": "Remark", + "no_copy": 1, + "oldfieldname": "remark", + "oldfieldtype": "Small Text", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "paid_loan", - "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": "Paid Loan", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "paid_loan", + "fieldtype": "Data", + "hidden": 1, + "label": "Paid Loan", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.voucher_type== \"Inter Company Journal Entry\"", - "fieldname": "inter_company_journal_entry_reference", - "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": "Inter Company Journal Entry Reference", - "length": 0, - "no_copy": 0, - "options": "Journal Entry", - "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 - }, + "depends_on": "eval:doc.voucher_type== \"Inter Company Journal Entry\"", + "fieldname": "inter_company_journal_entry_reference", + "fieldtype": "Link", + "label": "Inter Company Journal Entry Reference", + "options": "Journal Entry", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break98", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break98", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bill_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bill No", - "length": 0, - "no_copy": 0, - "oldfieldname": "bill_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bill_no", + "fieldtype": "Data", + "label": "Bill No", + "oldfieldname": "bill_no", + "oldfieldtype": "Data", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bill_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": "Bill Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "bill_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bill_date", + "fieldtype": "Date", + "label": "Bill Date", + "oldfieldname": "bill_date", + "oldfieldtype": "Date", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 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, - "oldfieldname": "due_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 - }, + "fieldname": "due_date", + "fieldtype": "Date", + "label": "Due Date", + "oldfieldname": "due_date", + "oldfieldtype": "Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", - "fieldname": "write_off", - "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": "Write Off", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", + "fieldname": "write_off", + "fieldtype": "Section Break", + "label": "Write Off" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Accounts Receivable", - "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", - "fieldname": "write_off_based_on", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Write Off Based On", - "length": 0, - "no_copy": 0, - "options": "Accounts Receivable\nAccounts Payable", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Accounts Receivable", + "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", + "fieldname": "write_off_based_on", + "fieldtype": "Select", + "label": "Write Off Based On", + "options": "Accounts Receivable\nAccounts Payable", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", - "fieldname": "get_outstanding_invoices", - "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": "Get Outstanding Invoices", - "length": 0, - "no_copy": 0, - "options": "get_outstanding_invoices", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", + "fieldname": "get_outstanding_invoices", + "fieldtype": "Button", + "label": "Get Outstanding Invoices", + "options": "get_outstanding_invoices", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_30", - "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_30", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", - "fieldname": "write_off_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": "Write Off Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", + "fieldname": "write_off_amount", + "fieldtype": "Currency", + "label": "Write Off Amount", + "options": "Company:company:default_currency", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "printing_settings", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Printing Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "printing_settings", + "fieldtype": "Section Break", + "label": "Printing Settings" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "pay_to_recd_from", - "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": "Pay To / Recd From", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "pay_to_recd_from", + "fieldtype": "Data", + "label": "Pay To / Recd From", + "no_copy": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_35", - "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_35", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "letter_head", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Letter Head", - "length": 0, - "no_copy": 0, - "options": "Letter Head", - "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_on_submit": 1, + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "options": "Letter Head" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_print_heading", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Heading", - "length": 0, - "no_copy": 1, - "oldfieldname": "select_print_heading", - "oldfieldtype": "Link", - "options": "Print Heading", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "select_print_heading", + "fieldtype": "Link", + "label": "Print Heading", + "no_copy": 1, + "oldfieldname": "select_print_heading", + "oldfieldtype": "Link", + "options": "Print Heading", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "addtional_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "addtional_info", + "fieldtype": "Section Break", + "label": "More Information", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mode_of_payment", - "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": "Mode of Payment", - "length": 0, - "no_copy": 0, - "options": "Mode of Payment", - "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": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_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": "Payment Order", - "length": 0, - "no_copy": 1, - "options": "Payment Order", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "payment_order", + "fieldtype": "Link", + "label": "Payment Order", + "no_copy": 1, + "options": "Payment Order", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break3", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "No", - "description": "", - "fieldname": "is_opening", - "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 Opening", - "length": 0, - "no_copy": 0, - "oldfieldname": "is_opening", - "oldfieldtype": "Select", - "options": "No\nYes", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "No", + "fieldname": "is_opening", + "fieldtype": "Select", + "label": "Is Opening", + "oldfieldname": "is_opening", + "oldfieldtype": "Select", + "options": "No\nYes", + "print_hide": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:in_list([\"Credit Note\", \"Debit Note\"], doc.voucher_type)", - "fieldname": "stock_entry", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Stock Entry", - "length": 0, - "no_copy": 0, - "options": "Stock Entry", - "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 - }, + "depends_on": "eval:in_list([\"Credit Note\", \"Debit Note\"], doc.voucher_type)", + "fieldname": "stock_entry", + "fieldtype": "Link", + "label": "Stock Entry", + "options": "Stock Entry", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subscription_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subscription Section", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "subscription_section", + "fieldtype": "Section Break", + "label": "Subscription Section" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "auto_repeat", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Auto Repeat", - "length": 0, - "no_copy": 1, - "options": "Auto Repeat", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "auto_repeat", + "fieldtype": "Link", + "label": "Auto Repeat", + "no_copy": 1, + "options": "Auto Repeat", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 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": "Link", - "options": "Journal Entry", - "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": "Link", + "options": "Journal Entry", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-file-text", - "idx": 176, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2019-01-07 16:52:02.838365", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Journal Entry", - "owner": "Administrator", + ], + "icon": "fa fa-file-text", + "idx": 176, + "is_submittable": 1, + "modified": "2020-01-16 13:05:30.634226", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Journal Entry", + "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": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Auditor", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor" } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 1, - "search_fields": "voucher_type,posting_date, due_date, cheque_no", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "title", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "search_fields": "voucher_type,posting_date, due_date, cheque_no", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "title", + "track_changes": 1 } \ No newline at end of file From 5511912251a0cf9714ff972042b2eb1dc9d2366f Mon Sep 17 00:00:00 2001 From: Himanshu Date: Thu, 16 Jan 2020 13:39:03 +0530 Subject: [PATCH 024/929] fix: don't hide primary_address_and_contact_detail (#20321) --- erpnext/selling/doctype/customer/customer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 68f75a03495..30220614e9b 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -103,7 +103,7 @@ frappe.ui.form.on("Customer", { } frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Customer'} - frm.toggle_display(['address_html','contact_html','primary_address_and_contact_detail'], !frm.doc.__islocal); + frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal); if(!frm.doc.__islocal) { frappe.contacts.render_address_and_contact(frm); From d16e7c096b04eac34d9692cd46ec91a56ebc2949 Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Fri, 17 Jan 2020 11:44:20 +0530 Subject: [PATCH 025/929] fix: salary structure assignment filter employee based on company (#20319) * fix: pick company from employee salary structure * fix: Salary structure assignment fetch company from enmployee * Revert "fix: Salary structure assignment fetch company from enmployee" This reverts commit ab2da691c79646d6d095f347ea3e273f466ee34f. * fix: Salary structure assignment fetch company from enmployee * fix: filter on company for salary structure assignment * fix: minor changes * fix: minor changes * fix: added company to salary strucutre assignment --- .../salary_structure/salary_structure.js | 4 ++-- .../salary_structure/salary_structure.py | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js index dd34ef2ae2c..672be0c2fc2 100755 --- a/erpnext/hr/doctype/salary_structure/salary_structure.js +++ b/erpnext/hr/doctype/salary_structure/salary_structure.js @@ -46,7 +46,7 @@ frappe.ui.form.on('Salary Structure', { frm.trigger("toggle_fields"); frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false); frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false); - + if(frm.doc.docstatus === 1) { frm.add_custom_button(__("Preview Salary Slip"), function() { frm.trigger('preview_salary_slip'); @@ -75,6 +75,7 @@ frappe.ui.form.on('Salary Structure', { title: __("Assign to Employees"), fields: [ {fieldname: "sec_break", fieldtype: "Section Break", label: __("Filter Employees By (Optional)")}, + {fieldname: "company", fieldtype: "Link", options: "Company", label: __("Company"), default: frm.doc.company, read_only:1}, {fieldname: "grade", fieldtype: "Link", options: "Employee Grade", label: __("Employee Grade")}, {fieldname:'department', fieldtype:'Link', options: 'Department', label: __('Department')}, {fieldname:'designation', fieldtype:'Link', options: 'Designation', label: __('Designation')}, @@ -87,7 +88,6 @@ frappe.ui.form.on('Salary Structure', { ], primary_action: function() { var data = d.get_values(); - frappe.call({ doc: frm.doc, method: "assign_salary_structure", diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py index 0e1a74f3702..568277f8a73 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/salary_structure.py @@ -81,24 +81,24 @@ class SalaryStructure(Document): return employees @frappe.whitelist() - def assign_salary_structure(self, grade=None, department=None, designation=None,employee=None, + def assign_salary_structure(self, company=None, grade=None, department=None, designation=None,employee=None, from_date=None, base=None,variable=None): - employees = self.get_employees(grade= grade,department= department,designation= designation,name=employee) + employees = self.get_employees(company= company, grade= grade,department= department,designation= designation,name=employee) if employees: if len(employees) > 20: frappe.enqueue(assign_salary_structure_for_employees, timeout=600, employees=employees, salary_structure=self,from_date=from_date, base=base,variable=variable) else: - assign_salary_structure_for_employees(employees, self,from_date=from_date, base=base,variable=variable) + assign_salary_structure_for_employees(employees, self, from_date=from_date, base=base,variable=variable) else: frappe.msgprint(_("No Employee Found")) -def assign_salary_structure_for_employees(employees, salary_structure,from_date=None, base=None,variable=None): +def assign_salary_structure_for_employees(employees, salary_structure, from_date=None, base=None,variable=None): salary_structures_assignments = [] - existing_assignments_for = get_existing_assignments(employees, salary_structure.name,from_date) + existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date) count=0 for employee in employees: if employee in existing_assignments_for: @@ -117,6 +117,7 @@ def create_salary_structures_assignment(employee, salary_structure, from_date, b assignment = frappe.new_doc("Salary Structure Assignment") assignment.employee = employee assignment.salary_structure = salary_structure.name + assignment.company = salary_structure.company assignment.from_date = from_date assignment.base = base assignment.variable = variable @@ -125,12 +126,12 @@ def create_salary_structures_assignment(employee, salary_structure, from_date, b return assignment.name -def get_existing_assignments(employees, salary_structure,from_date): +def get_existing_assignments(employees, salary_structure, from_date): salary_structures_assignments = frappe.db.sql_list(""" select distinct employee from `tabSalary Structure Assignment` where salary_structure=%s and employee in (%s) - and from_date=%s and docstatus=1 - """ % ('%s', ', '.join(['%s']*len(employees)),'%s'), [salary_structure] + employees+[from_date]) + and from_date=%s and company= %s and docstatus=1 + """ % ('%s', ', '.join(['%s']*len(employees)),'%s', '%s'), [salary_structure.name] + employees+[from_date]+[salary_structure.company]) if salary_structures_assignments: frappe.msgprint(_("Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}") .format("\n".join(salary_structures_assignments))) @@ -170,7 +171,7 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print = def get_employees(salary_structure): employees = frappe.get_list('Salary Structure Assignment', filters={'salary_structure': salary_structure, 'docstatus': 1}, fields=['employee']) - + if not employees: frappe.throw(_("There's no Employee with Salary Structure: {0}. \ Assign {1} to an Employee to preview Salary Slip").format(salary_structure, salary_structure)) From c2b9598966d164732fb0137f88c86d583feec75e Mon Sep 17 00:00:00 2001 From: Pranav Nachnekar Date: Fri, 17 Jan 2020 06:15:03 +0000 Subject: [PATCH 026/929] fix: imporer escaping (#20329) --- .../doctype/authorization_control/authorization_control.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/authorization_control/authorization_control.py b/erpnext/setup/doctype/authorization_control/authorization_control.py index 6a184b1a166..55e1ed0d3a7 100644 --- a/erpnext/setup/doctype/authorization_control/authorization_control.py +++ b/erpnext/setup/doctype/authorization_control/authorization_control.py @@ -76,7 +76,7 @@ class AuthorizationControl(TransactionBase): add_cond = '' auth_value = av_dis - if val == 1: add_cond += " and system_user = '"+ frappe.db.escape(session['user']) +"'" + if val == 1: add_cond += " and system_user = {0}".format(frappe.db.escape(session['user'])) elif val == 2: add_cond += " and system_role IN %s" % ("('"+"','".join(frappe.get_roles())+"')") else: add_cond += " and ifnull(system_user,'') = '' and ifnull(system_role,'') = ''" @@ -85,7 +85,7 @@ class AuthorizationControl(TransactionBase): if doc_obj: if doc_obj.doctype == 'Sales Invoice': customer = doc_obj.customer else: customer = doc_obj.customer_name - add_cond = " and master_name = '"+ frappe.db.escape(customer) +"'" + add_cond = " and master_name = {0}".format(frappe.db.escape(customer)) if based_on == 'Itemwise Discount': if doc_obj: for t in doc_obj.get("items"): From 4dfff5a2719ed84a24e7d9b54869f274899bb9f6 Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 17 Jan 2020 11:46:00 +0530 Subject: [PATCH 027/929] fix: handle default value for batch size in BOM operation (#20327) --- erpnext/manufacturing/doctype/bom/bom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 98f10059712..3561f3637df 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -588,7 +588,7 @@ class BOM(WebsiteGenerator): for d in self.operations: if not d.description: d.description = frappe.db.get_value('Operation', d.operation, 'description') - if not d.batch_size > 0: + if not d.batch_size or d.batch_size <= 0: d.batch_size = 1 def get_list_context(context): From d85e144944e1689dd689988ce915fbd0e0564a09 Mon Sep 17 00:00:00 2001 From: "Parth J. Kharwar" Date: Fri, 17 Jan 2020 11:47:49 +0530 Subject: [PATCH 028/929] chore: changed employee contact field name (#20227) --- erpnext/hr/doctype/employee/employee.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index 9291820524e..4a60dba30e5 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -265,7 +265,7 @@ "bold": 1, "fieldname": "person_to_be_contacted", "fieldtype": "Data", - "label": "Emergency Contact" + "label": "Emergency Contact Name" }, { "fieldname": "relation", @@ -782,7 +782,7 @@ "icon": "fa fa-user", "idx": 24, "image_field": "image", - "modified": "2019-09-12 14:21:12.711280", + "modified": "2020-01-09 04:23:55.611366", "modified_by": "Administrator", "module": "HR", "name": "Employee", From 4b893b1d5138e9746693f5e16c1a63910cc1bf1c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 17 Jan 2020 15:57:43 +0530 Subject: [PATCH 029/929] fix: Plaid TypeError in add_bank_accounts (#20336) --- .../doctype/plaid_settings/plaid_settings.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index 4af1d740946..a04d6c5be73 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -53,7 +53,11 @@ def add_institution(token, response): @frappe.whitelist() def add_bank_accounts(response, bank, company): - response = json.loads(response) if not "accounts" in response else response + try: + response = json.loads(response) + except TypeError: + pass + bank = json.loads(bank) result = [] From 15963525853ddeddad23a9eb90a2a08d9513d839 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 17 Jan 2020 15:59:02 +0530 Subject: [PATCH 030/929] fix: close_opportunity_after_days field is in selling settings (#20334) --- erpnext/crm/doctype/opportunity/opportunity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 4e0eebd0de9..12f8fb95ff4 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -308,7 +308,7 @@ def set_multiple_status(names, status): def auto_close_opportunity(): """ auto close the `Replied` Opportunities after 7 days """ - auto_close_after_days = frappe.db.get_value("Support Settings", "Support Settings", "close_opportunity_after_days") or 15 + auto_close_after_days = frappe.db.get_single_value("Selling Settings", "close_opportunity_after_days") or 15 opportunities = frappe.db.sql(""" select name from tabOpportunity where status='Replied' and modified Date: Mon, 20 Jan 2020 15:44:48 +0530 Subject: [PATCH 031/929] fix: deprecated fetching item price based on min_qty (#20346) --- erpnext/stock/doctype/item_price/item_price.py | 4 ++-- .../stock/doctype/item_price/test_item_price.py | 8 +------- erpnext/stock/get_item_details.py | 17 ++--------------- 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index 4c496cb59a7..957c41546b3 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -13,7 +13,7 @@ from frappe.model.document import Document class ItemPrice(Document): - + def validate(self): self.validate_item() self.validate_dates() @@ -51,7 +51,7 @@ class ItemPrice(Document): def check_duplicates(self): conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s" - for field in ['uom', 'min_qty', 'valid_from', + for field in ['uom', 'valid_from', 'valid_upto', 'packing_unit', 'customer', 'supplier']: if self.get(field): conditions += " and {0} = %({1})s".format(field, field) diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py index 3782f540cf2..702acc38fe4 100644 --- a/erpnext/stock/doctype/item_price/test_item_price.py +++ b/erpnext/stock/doctype/item_price/test_item_price.py @@ -21,7 +21,7 @@ class TestItemPrice(unittest.TestCase): def test_addition_of_new_fields(self): # Based on https://github.com/frappe/erpnext/issues/8456 test_fields_existance = [ - 'supplier', 'customer', 'uom', 'min_qty', 'lead_time_days', + 'supplier', 'customer', 'uom', 'lead_time_days', 'packing_unit', 'valid_from', 'valid_upto', 'note' ] doc_fields = frappe.copy_doc(test_records[1]).__dict__.keys() @@ -43,7 +43,6 @@ class TestItemPrice(unittest.TestCase): args = { "price_list": doc.price_list, - "min_qty": doc.min_qty, "customer": doc.customer, "uom": "_Test UOM", "transaction_date": '2017-04-18', @@ -58,7 +57,6 @@ class TestItemPrice(unittest.TestCase): doc = frappe.copy_doc(test_records[2]) args = { "price_list": doc.price_list, - "min_qty": 30, "customer": doc.customer, "uom": "_Test UOM", "transaction_date": '2017-04-18', @@ -74,7 +72,6 @@ class TestItemPrice(unittest.TestCase): args = { "price_list": doc.price_list, - "min_qty": doc.min_qty, "customer": "_Test Customer", "uom": "_Test UOM", "transaction_date": '2017-04-18', @@ -90,7 +87,6 @@ class TestItemPrice(unittest.TestCase): args = { "price_list": doc.price_list, - "min_qty": doc.min_qty, "qty": 7, "uom": "_Test UOM", "transaction_date": "01-15-2019" @@ -105,7 +101,6 @@ class TestItemPrice(unittest.TestCase): args = { "price_list": doc.price_list, - "min_qty": doc.min_qty, "customer": "_Test Customer", "uom": "_Test UOM", "transaction_date": "2017-04-25", @@ -121,7 +116,6 @@ class TestItemPrice(unittest.TestCase): args = { "price_list": doc.price_list, - "min_qty": doc.min_qty, "uom": "_Test UOM", "qty": 7, } diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index dec3801feaa..b76a9b064a6 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -581,7 +581,7 @@ def get_item_price(args, item_code, ignore_party=False): Get name, price_list_rate from Item Price based on conditions Check if the desired qty is within the increment of the packing list. :param args: dict (or frappe._dict) with mandatory fields price_list, uom - optional fields min_qty, transaction_date, customer, supplier + optional fields transaction_date, customer, supplier :param item_code: str, Item Doctype field item_code """ @@ -599,24 +599,16 @@ def get_item_price(args, item_code, ignore_party=False): else: conditions += " and (customer is null or customer = '') and (supplier is null or supplier = '')" - if args.get('min_qty'): - conditions += " and ifnull(min_qty, 0) <= %(min_qty)s" - if args.get('transaction_date'): conditions += """ and %(transaction_date)s between ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')""" return frappe.db.sql(""" select name, price_list_rate, uom from `tabItem Price` {conditions} - order by valid_from desc, min_qty desc, uom desc """.format(conditions=conditions), args) + order by valid_from desc, uom desc """.format(conditions=conditions), args) def get_price_list_rate_for(args, item_code): """ - Return Price Rate based on min_qty of each Item Price Rate.\ - For example, desired qty is 10 and Item Price Rates exists - for min_qty 9 and min_qty 20. It returns Item Price Rate for qty 9 as - the best fit in the range of avaliable min_qtyies - :param customer: link to Customer DocType :param supplier: link to Supplier DocType :param price_list: str (Standard Buying or Standard Selling) @@ -630,8 +622,6 @@ def get_price_list_rate_for(args, item_code): "customer": args.get('customer'), "supplier": args.get('supplier'), "uom": args.get('uom'), - "min_qty": args.get('qty') if args.get('price_list_uom_dependant')\ - else flt(args.get('qty')) * flt(args.get("conversion_factor", 1)), "transaction_date": args.get('transaction_date'), } @@ -647,9 +637,6 @@ def get_price_list_rate_for(args, item_code): general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party")) - if not general_price_list_rate: - del item_price_args["min_qty"] - general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party")) if not general_price_list_rate and args.get("uom") != args.get("stock_uom"): item_price_args["uom"] = args.get("stock_uom") From 19c5e031a94c2c0082c1bad1e735c5f7459280ad Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 20 Jan 2020 17:52:53 +0530 Subject: [PATCH 032/929] fix: incorrect number of gl entries issue (#20350) --- erpnext/manufacturing/doctype/bom/bom.py | 22 ++++++++++--------- .../stock/doctype/stock_entry/stock_entry.py | 2 ++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 3561f3637df..7f7ffa14bfa 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -782,11 +782,12 @@ def add_non_stock_items_cost(stock_entry, work_order, expense_account): for name in non_stock_items: non_stock_items_cost += flt(items.get(name[0])) * flt(stock_entry.fg_completed_qty) / flt(bom.quantity) - stock_entry.append('additional_costs', { - 'expense_account': expense_account, - 'description': _("Non stock items"), - 'amount': non_stock_items_cost - }) + if non_stock_items_cost: + stock_entry.append('additional_costs', { + 'expense_account': expense_account, + 'description': _("Non stock items"), + 'amount': non_stock_items_cost + }) def add_operations_cost(stock_entry, work_order=None, expense_account=None): from erpnext.stock.doctype.stock_entry.stock_entry import get_operating_cost_per_unit @@ -803,11 +804,12 @@ def add_operations_cost(stock_entry, work_order=None, expense_account=None): additional_operating_cost_per_unit = \ flt(work_order.additional_operating_cost) / flt(work_order.qty) - stock_entry.append('additional_costs', { - "expense_account": expense_account, - "description": "Additional Operating Cost", - "amount": additional_operating_cost_per_unit * flt(stock_entry.fg_completed_qty) - }) + if additional_operating_cost_per_unit: + stock_entry.append('additional_costs', { + "expense_account": expense_account, + "description": "Additional Operating Cost", + "amount": additional_operating_cost_per_unit * flt(stock_entry.fg_completed_qty) + }) @frappe.whitelist() def get_bom_diff(bom1, bom2): diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 996727042e3..9892e7571d4 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -682,6 +682,8 @@ class StockEntry(StockController): if item_account_wise_additional_cost: for d in self.get("items"): for account, amount in iteritems(item_account_wise_additional_cost.get((d.item_code, d.name), {})): + if not amount: continue + gl_entries.append(self.get_gl_dict({ "account": account, "against": d.expense_account, From 541881162ac9fa1faf88bea25756a0b839bbf5eb Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 20 Jan 2020 19:42:49 +0530 Subject: [PATCH 033/929] fix: calculate remaining leave balance (#20342) * fix: calculate remaining leave balance * fix: nonetype issue in the total leaves calculation --- erpnext/hr/doctype/leave_application/leave_application.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 915cea149d8..8c109464fdf 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -520,8 +520,7 @@ def get_pending_leaves_for_period(employee, leave_type, from_date, to_date): def get_remaining_leaves(allocation, leaves_taken, date, expiry): ''' Returns minimum leaves remaining after comparing with remaining days for allocation expiry ''' - def _get_remaining_leaves(allocated_leaves, end_date): - remaining_leaves = flt(allocated_leaves) + flt(leaves_taken) + def _get_remaining_leaves(remaining_leaves, end_date): if remaining_leaves > 0: remaining_days = date_diff(end_date, date) + 1 @@ -529,10 +528,11 @@ def get_remaining_leaves(allocation, leaves_taken, date, expiry): return remaining_leaves - total_leaves = allocation.total_leaves_allocated + total_leaves = flt(allocation.total_leaves_allocated) + flt(leaves_taken) if expiry and allocation.unused_leaves: - remaining_leaves = _get_remaining_leaves(allocation.unused_leaves, expiry) + remaining_leaves = flt(allocation.unused_leaves) + flt(leaves_taken) + remaining_leaves = _get_remaining_leaves(remaining_leaves, expiry) total_leaves = flt(allocation.new_leaves_allocated) + flt(remaining_leaves) From 05f4be69f4573d7013cba2a66f3c82ca9a3deadf Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 21 Jan 2020 13:04:26 +0530 Subject: [PATCH 034/929] fix: Don't fetch price list rate on change of qty (#20362) --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 512bcf9f4fb..8870d75e39a 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -968,7 +968,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ qty: function(doc, cdt, cdn) { let item = frappe.get_doc(cdt, cdn); - this.conversion_factor(doc, cdt, cdn, false); + this.conversion_factor(doc, cdt, cdn, true); this.apply_pricing_rule(item, true); }, From c32249a72f4201eb4209f56c0d16f99475df656d Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Tue, 21 Jan 2020 14:12:49 +0530 Subject: [PATCH 035/929] fix: Set Quotation expired if not Ordered or Lost (#20365) Co-authored-by: Chinmay Pai Co-authored-by: KanchanChauhan --- erpnext/selling/doctype/quotation/quotation.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 488331a8102..00471c762c8 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -185,8 +185,12 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): return doclist def set_expired_status(): - frappe.db.sql("""UPDATE `tabQuotation` SET `status` = 'Expired' - WHERE `status` != "Expired" AND `valid_till` < %s""", (nowdate())) + frappe.db.sql(""" + UPDATE + `tabQuotation` SET `status` = 'Expired' + WHERE + `status` not in ('Ordered', 'Expired', 'Lost', 'Cancelled') AND `valid_till` < %s + """, (nowdate())) @frappe.whitelist() def make_sales_invoice(source_name, target_doc=None): From c759b06f16b715b432d1c78ee8542a5d4a9876c1 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 21 Jan 2020 15:26:46 +0530 Subject: [PATCH 036/929] fix: account not added to child company (#20364) * fix: account not added to child company * fix: removed print Co-authored-by: Nabin Hait --- erpnext/accounts/doctype/account/account.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index cf1748f6a7f..1407d5f5fe3 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -95,29 +95,29 @@ class Account(NestedSet): # ignore validation while creating new compnay or while syncing to child companies if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation: return - ancestors = get_root_company(self.company) if ancestors: if frappe.get_value("Company", self.company, "allow_account_creation_against_child_company"): return - if not frappe.db.get_value("Account", {'account_name': self.account_name, 'company': ancestors[0]}, 'name'): frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0])) else: descendants = get_descendants_of('Company', self.company) if not descendants: return - parent_acc_name_map = {} parent_acc_name, parent_acc_number = frappe.db.get_value('Account', self.parent_account, \ ["account_name", "account_number"]) - for d in frappe.db.get_values('Account', - { "company": ["in", descendants], "account_name": parent_acc_name, - "account_number": parent_acc_number }, - ["company", "name"], as_dict=True): + filters = { + "company": ["in", descendants], + "account_name": parent_acc_name, + } + if parent_acc_number: + filters["account_number"] = parent_acc_number + + for d in frappe.db.get_values('Account', filters=filters, fieldname=["company", "name"], as_dict=True): parent_acc_name_map[d["company"]] = d["name"] if not parent_acc_name_map: return - self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name) def validate_group_or_ledger(self): @@ -175,7 +175,6 @@ class Account(NestedSet): filters["account_number"] = self.account_number child_account = frappe.db.get_value("Account", filters, 'name') - if not child_account: doc = frappe.copy_doc(self) doc.flags.ignore_root_company_validation = True From 87fe2143bee4ac4ada6152b35ba348110c1e6c26 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 21 Jan 2020 15:32:12 +0530 Subject: [PATCH 037/929] fix: not able to import chart of account if parent account is missing (#20367) --- .../chart_of_accounts_importer.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 9bf5887b38f..7f51b139de4 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 @@ -96,7 +96,11 @@ def build_forest(data): if parent_account == account_name == child: return [parent_account] elif account_name == child: - return [child] + return_parent(data, parent_account) + parent_account_list = return_parent(data, parent_account) + if not parent_account_list: + frappe.throw(_("The parent account {0} does not exists") + .format(parent_account)) + return [child] + parent_account_list charts_map, paths = {}, [] From 813726b4152828ff767a558867dcc884b62d0efb Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 21 Jan 2020 19:22:27 +0530 Subject: [PATCH 038/929] fix: Zero division error while making finished good entry against the work order --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 9892e7571d4..8f1d8ef8546 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -484,7 +484,7 @@ class StockEntry(StockController): if self.work_order \ and frappe.db.get_single_value("Manufacturing Settings", "material_consumption"): bom_items = self.get_bom_raw_materials(d.transfer_qty) - raw_material_cost = sum([flt(d.qty)*flt(d.rate) for d in bom_items.values()]) + raw_material_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()]) if raw_material_cost: d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate")) From f38d9d8f0dbec4457bf9068b0f7d32b69fe95066 Mon Sep 17 00:00:00 2001 From: thefalconx33 Date: Wed, 22 Jan 2020 12:22:55 +0530 Subject: [PATCH 039/929] fix: tuple index out of range error --- erpnext/assets/doctype/asset/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 86b5a11a1d1..0df6a6c7de0 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -620,7 +620,7 @@ def get_asset_account(account_name, asset=None, asset_category=None, company=Non if not account: if not asset_category: - frappe.throw(_("Set {0} in company {2}").format(account_name.replace('_', ' ').title(), company)) + frappe.throw(_("Set {0} in company {1}").format(account_name.replace('_', ' ').title(), company)) else: frappe.throw(_("Set {0} in asset category {1} or company {2}") .format(account_name.replace('_', ' ').title(), asset_category, company)) From 61b9fe6a81ea890fb5062b83800c490973e4c77e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 22 Jan 2020 14:43:37 +0530 Subject: [PATCH 040/929] fix: lead date comparison issue --- erpnext/crm/doctype/lead/lead.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 1dae4b9fc1c..e037d272930 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -53,8 +53,8 @@ class Lead(SellingController): if self.contact_date and getdate(self.contact_date) < getdate(nowdate()): frappe.throw(_("Next Contact Date cannot be in the past")) - if self.ends_on and self.contact_date and\ - (self.ends_on < self.contact_date): + if (self.ends_on and self.contact_date and + (getdate(self.ends_on) < getdate(self.contact_date))): frappe.throw(_("Ends On date cannot be before Next Contact Date.")) def on_update(self): From 79f630661a85222191e6f897ec6ca6210aca546b Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 22 Jan 2020 16:53:30 +0530 Subject: [PATCH 041/929] fix: lead details duplicate entries --- .../crm/report/lead_details/lead_details.json | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/erpnext/crm/report/lead_details/lead_details.json b/erpnext/crm/report/lead_details/lead_details.json index 17800fd4bc9..cdeb6bbe384 100644 --- a/erpnext/crm/report/lead_details/lead_details.json +++ b/erpnext/crm/report/lead_details/lead_details.json @@ -1,28 +1,29 @@ { - "add_total_row": 0, - "creation": "2013-10-22 11:58:16", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2018-09-26 18:59:46.520731", - "modified_by": "Administrator", - "module": "CRM", - "name": "Lead Details", - "owner": "Administrator", - "prepared_report": 0, - "query": "SELECT\n `tabLead`.name as \"Lead Id:Link/Lead:120\",\n `tabLead`.lead_name as \"Lead Name::120\",\n\t`tabLead`.company_name as \"Company Name::120\",\n\t`tabLead`.status as \"Status::120\",\n\tconcat_ws(', ', \n\t\ttrim(',' from `tabAddress`.address_line1), \n\t\ttrim(',' from tabAddress.address_line2)\n\t) as 'Address::180',\n\t`tabAddress`.state as \"State::100\",\n\t`tabAddress`.pincode as \"Pincode::70\",\n\t`tabAddress`.country as \"Country::100\",\n\t`tabLead`.phone as \"Phone::100\",\n\t`tabLead`.mobile_no as \"Mobile No::100\",\n\t`tabLead`.email_id as \"Email Id::120\",\n\t`tabLead`.lead_owner as \"Lead Owner::120\",\n\t`tabLead`.source as \"Source::120\",\n\t`tabLead`.territory as \"Territory::120\",\n\t`tabLead`.notes as \"Notes::360\",\n `tabLead`.owner as \"Owner:Link/User:120\"\nFROM\n\t`tabLead`\n\tleft join `tabDynamic Link` on (\n\t\t`tabDynamic Link`.link_name=`tabLead`.name\n\t)\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.name=`tabDynamic Link`.parent\n\t)\nWHERE\n\t`tabLead`.docstatus<2\nORDER BY\n\t`tabLead`.name asc", - "ref_doctype": "Lead", - "report_name": "Lead Details", - "report_type": "Query Report", + "add_total_row": 0, + "creation": "2013-10-22 11:58:16", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "modified": "2020-01-22 16:51:56.591110", + "modified_by": "Administrator", + "module": "CRM", + "name": "Lead Details", + "owner": "Administrator", + "prepared_report": 0, + "query": "SELECT\n `tabLead`.name as \"Lead Id:Link/Lead:120\",\n `tabLead`.lead_name as \"Lead Name::120\",\n\t`tabLead`.company_name as \"Company Name::120\",\n\t`tabLead`.status as \"Status::120\",\n\tconcat_ws(', ', \n\t\ttrim(',' from `tabAddress`.address_line1), \n\t\ttrim(',' from tabAddress.address_line2)\n\t) as 'Address::180',\n\t`tabAddress`.state as \"State::100\",\n\t`tabAddress`.pincode as \"Pincode::70\",\n\t`tabAddress`.country as \"Country::100\",\n\t`tabLead`.phone as \"Phone::100\",\n\t`tabLead`.mobile_no as \"Mobile No::100\",\n\t`tabLead`.email_id as \"Email Id::120\",\n\t`tabLead`.lead_owner as \"Lead Owner::120\",\n\t`tabLead`.source as \"Source::120\",\n\t`tabLead`.territory as \"Territory::120\",\n\t`tabLead`.notes as \"Notes::360\",\n `tabLead`.owner as \"Owner:Link/User:120\"\nFROM\n\t`tabLead`\n\tleft join `tabDynamic Link` on (\n\t\t`tabDynamic Link`.link_name=`tabLead`.name \n\t\tand `tabDynamic Link`.parenttype = 'Address'\n\t)\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.name=`tabDynamic Link`.parent\n\t)\nWHERE\n\t`tabLead`.docstatus<2\nORDER BY\n\t`tabLead`.name asc", + "ref_doctype": "Lead", + "report_name": "Lead Details", + "report_type": "Query Report", "roles": [ { "role": "Sales User" - }, + }, { "role": "Sales Manager" - }, + }, { "role": "System Manager" } From 144012a5de1ea1f7deb6750f333b3b26e1798c08 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 23 Jan 2020 12:42:42 +0530 Subject: [PATCH 042/929] fix: unlink references from batch on cacnellation of stock transactions --- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 1 + erpnext/controllers/stock_controller.py | 7 +++++++ erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 1 + erpnext/stock/doctype/stock_entry/stock_entry.py | 1 + 4 files changed, 10 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 1a14a2a732d..261b0f03f7a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -866,6 +866,7 @@ class PurchaseInvoice(BuyingController): # because updating ordered qty in bin depends upon updated ordered qty in PO if self.update_stock == 1: self.update_stock_ledger() + self.unlink_reference_from_batch() self.make_gl_entries_on_cancel() self.update_project() diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index aa3ce9d4d04..723ede87a30 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -235,6 +235,13 @@ class StockController(AccountsController): frappe.throw(_("{0} {1}: Cost Center is mandatory for Item {2}").format( _(self.doctype), self.name, item.get("item_code"))) + def unlink_reference_from_batch(self): + frappe.db.sql(""" UPDATE `tabBatch` + SET reference_doctype = NULL, reference_name = NULL + WHERE + reference_doctype = %s and reference_name = %s + """, (self.doctype, self.name)) + def get_sl_entries(self, d, args): sl_dict = frappe._dict({ "item_code": d.get("item_code", None), diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 691f92ffa72..e068dd7610f 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -195,6 +195,7 @@ class PurchaseReceipt(BuyingController): # because updating ordered qty in bin depends upon updated ordered qty in PO self.update_stock_ledger() self.make_gl_entries_on_cancel() + self.unlink_reference_from_batch() def get_current_stock(self): for d in self.get('supplied_items'): diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 9892e7571d4..89bcc9300f9 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -110,6 +110,7 @@ class StockEntry(StockController): self.update_cost_in_project() self.update_transferred_qty() self.update_quality_inspection() + self.unlink_reference_from_batch() def set_job_card_data(self): if self.job_card and not self.work_order: From 89f42eef49a9db1404997b216d827fd357947677 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 23 Jan 2020 15:49:39 +0530 Subject: [PATCH 043/929] fix: add serial no button not working --- erpnext/public/js/utils/serial_no_batch_selector.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index da731950b1a..4ec12ce8e7f 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -316,7 +316,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ frappe.call({ method: 'erpnext.stock.doctype.batch.batch.get_batch_qty', args: { - batch_no: this.doc.batch_no, + batch_no: me.item.batch_no, warehouse: me.warehouse_details.name, item_code: me.item_code }, @@ -392,6 +392,10 @@ erpnext.SerialNoBatchSelector = Class.extend({ delivery_document_no: "" } + if (this.has_batch) { + serial_no_filters["batch_no"] = this.item.batch_no; + } + if (me.warehouse_details.name) { serial_no_filters['warehouse'] = me.warehouse_details.name; } From 5860427bc58682cbe75ed46dee3fc8c392a15394 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 23 Jan 2020 17:36:52 +0530 Subject: [PATCH 044/929] delete the auto created batch --- .../doctype/purchase_invoice/purchase_invoice.py | 2 +- erpnext/controllers/stock_controller.py | 16 ++++++++++------ .../doctype/purchase_receipt/purchase_receipt.py | 2 +- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 261b0f03f7a..d4676ff0375 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -866,7 +866,7 @@ class PurchaseInvoice(BuyingController): # because updating ordered qty in bin depends upon updated ordered qty in PO if self.update_stock == 1: self.update_stock_ledger() - self.unlink_reference_from_batch() + self.delete_auto_created_batches() self.make_gl_entries_on_cancel() self.update_project() diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 723ede87a30..2b47a2c09b8 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -235,12 +235,16 @@ class StockController(AccountsController): frappe.throw(_("{0} {1}: Cost Center is mandatory for Item {2}").format( _(self.doctype), self.name, item.get("item_code"))) - def unlink_reference_from_batch(self): - frappe.db.sql(""" UPDATE `tabBatch` - SET reference_doctype = NULL, reference_name = NULL - WHERE - reference_doctype = %s and reference_name = %s - """, (self.doctype, self.name)) + def delete_auto_created_batches(self): + for d in self.items: + if not d.batch_no: continue + + d.batch_no = None + d.db_set("batch_no", None) + + for data in frappe.get_all("Batch", + {'reference_name': self.name, 'reference_doctype': self.doctype}): + frappe.delete_doc("Batch", data.name) def get_sl_entries(self, d, args): sl_dict = frappe._dict({ diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index e068dd7610f..09adb567513 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -195,7 +195,7 @@ class PurchaseReceipt(BuyingController): # because updating ordered qty in bin depends upon updated ordered qty in PO self.update_stock_ledger() self.make_gl_entries_on_cancel() - self.unlink_reference_from_batch() + self.delete_auto_created_batches() def get_current_stock(self): for d in self.get('supplied_items'): diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 89bcc9300f9..cf01d3253ee 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -110,7 +110,7 @@ class StockEntry(StockController): self.update_cost_in_project() self.update_transferred_qty() self.update_quality_inspection() - self.unlink_reference_from_batch() + self.delete_auto_created_batches() def set_job_card_data(self): if self.job_card and not self.work_order: From ba4f565ca37567f2944b1c96983c2069e172fec7 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 23 Jan 2020 17:42:57 +0530 Subject: [PATCH 045/929] fix: Order GL entries by creation time in General ledger --- erpnext/accounts/report/general_ledger/general_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index ec3fb1fc9c2..b32a54f5cc1 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -119,7 +119,7 @@ def get_gl_entries(filters): select_fields = """, debit, credit, debit_in_account_currency, credit_in_account_currency """ - order_by_statement = "order by posting_date, account" + order_by_statement = "order by posting_date, account, creation" if filters.get("group_by") == _("Group by Voucher"): order_by_statement = "order by posting_date, voucher_type, voucher_no" From 245fc49089f68b28da23b6adba37639574dc9eb3 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 24 Jan 2020 15:50:40 +0530 Subject: [PATCH 046/929] fix: healthcare settings registration fee NoneType error (#20396) --- .../healthcare_settings.json | 1238 ++--------------- 1 file changed, 95 insertions(+), 1143 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json index 3bd2501a264..f6521183097 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json @@ -1,1368 +1,320 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, + "actions": [], "beta": 1, "creation": "2017-05-09 11:26:22.337760", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "sb_op_settings", + "patient_master_name", + "manage_customer", + "default_medical_code_standard", + "column_break_9", + "collect_registration_fee", + "registration_fee", + "manage_appointment_invoice_automatically", + "max_visit", + "valid_days", + "healthcare_service_items", + "inpatient_visit_charge_item", + "op_consulting_charge_item", + "column_break_13", + "clinical_procedure_consumable_item", + "out_patient_sms_alerts", + "reg_sms", + "reg_msg", + "app_con", + "app_con_msg", + "no_con", + "column_break_16", + "app_rem", + "app_rem_msg", + "rem_before", + "sb_in_ac", + "income_account", + "sb_r_ac", + "receivable_account", + "sb_lab_settings", + "create_test_on_si_submit", + "require_sample_collection", + "require_test_result_approval", + "column_break_34", + "employee_name_and_designation_in_print", + "custom_signature_in_print", + "laboratory_sms_alerts", + "sms_printed", + "column_break_28", + "sms_emailed" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sb_op_settings", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Out Patient Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Out Patient Settings" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "patient_master_name", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Patient Name By", - "length": 0, - "no_copy": 0, - "options": "Patient Name\nNaming Series", - "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": "Patient Name\nNaming Series" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "description": "If checked, a customer will be created, mapped to Patient.\nPatient Invoices will be created against this Customer. You can also select existing Customer while creating Patient.", "fieldname": "manage_customer", "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": "Manage Customer", - "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": "Manage Customer" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "default_medical_code_standard", "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 Medical Code Standard", - "length": 0, - "no_copy": 0, - "options": "Medical Code Standard", - "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": "Medical Code Standard" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_9", - "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, + "default": "0", "fieldname": "collect_registration_fee", "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 Fee for Patient Registration", - "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": "Collect Fee for Patient Registration" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "collect_registration_fee", "fieldname": "registration_fee", "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": "Registration Fee", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "mandatory_depends_on": "eval:doc.collect_registration_fee == 1", + "options": "Currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", "description": "Manage Appointment Invoice submit and cancel automatically for Patient Encounter", "fieldname": "manage_appointment_invoice_automatically", "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": "Invoice Appointments Automatically", - "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": "Invoice Appointments Automatically" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "max_visit", "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": "Patient Encounters in valid 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": "Patient Encounters in valid days" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "valid_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": "Valid number of 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": "Valid number of days" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "healthcare_service_items", "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": "Healthcare Service Items", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Healthcare Service Items" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "inpatient_visit_charge_item", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Inpatient Visit Charge Item", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "op_consulting_charge_item", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Out Patient Consulting Charge Item", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_13", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "clinical_procedure_consumable_item", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Clinical Procedure Consumable Item", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "out_patient_sms_alerts", "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": "Out Patient SMS Alerts", - "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": "Out Patient SMS Alerts" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "reg_sms", "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": "Patient Registration", - "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": "Patient Registration" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Hello {{doc.patient}}, Thank you for registering with {{doc.company}}. Your ID is {{doc.id}} . Please note this ID for future reference. \nThank You, Get well soon!", "depends_on": "reg_sms", "fieldname": "reg_msg", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Registration 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": "Registration Message" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "app_con", "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": "Appointment Confirmation", - "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": "Appointment Confirmation" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Hello {{doc.patient}}, You have scheduled an appointment with {{doc.practitioner}} by {{doc.start_dt}} at {{doc.company}}.\nThank you, Good day!", "depends_on": "app_con", "fieldname": "app_con_msg", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Confirmation 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": "Confirmation Message" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "app_con", "description": "Do not confirm if appointment is created for the same day", "fieldname": "no_con", "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": "Avoid Confirmation", - "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": "Avoid Confirmation" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_16", - "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, + "default": "0", "fieldname": "app_rem", "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": "Appointment Reminder", - "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": "Appointment Reminder" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Hello {{doc.patient}}, You have an appointment with {{doc.practitioner}} by {{doc.appointment_time}} at {{doc.company}}.\nThank you, Good day!\n", "depends_on": "app_rem", "fieldname": "app_rem_msg", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reminder 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": "Reminder Message" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "app_rem", "fieldname": "rem_before", "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": "Remind Before", - "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": "Remind Before" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "description": "Default income accounts to be used if not set in Healthcare Practitioner to book Appointment charges.", "fieldname": "sb_in_ac", "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": "Income Account", - "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": "Income Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "income_account", "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": "Income Account", - "length": 0, - "no_copy": 0, - "options": "Party Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Party Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "description": "Default receivable accounts to be used if not set in Patient to book Appointment charges.", "fieldname": "sb_r_ac", "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": "Receivable Account", - "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": "Receivable Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "receivable_account", "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": "Receivable Account", - "length": 0, - "no_copy": 0, - "options": "Party Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Party Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "sb_lab_settings", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Laboratory Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Laboratory Settings" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "create_test_on_si_submit", "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": "Create Lab Test(s) on Sales Invoice Submit", - "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": "Create Lab Test(s) on Sales Invoice Submit" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "description": "Create documents for sample collection", "fieldname": "require_sample_collection", "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": "Manage Sample Collection", - "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": "Manage Sample Collection" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "require_test_result_approval", "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": "Require Lab Test Approval", - "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": "Require Lab Test Approval" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_34", - "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, "default": "1", "fieldname": "employee_name_and_designation_in_print", "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": "Employee name and designation in print", - "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": "Employee name and designation in print" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.employee_name_and_designation_in_print == '0'\n", "fieldname": "custom_signature_in_print", "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": "Custom Signature in Print", - "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": "Custom Signature in Print" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "laboratory_sms_alerts", "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": "Laboratory SMS Alerts", - "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": "Laboratory SMS Alerts" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Hello {{doc.patient}}, Your {{doc.lab_test_name}} result is ready with {{doc.company }}. \nThank You, Good day!", "fieldname": "sms_printed", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Result Printed", - "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": "Result Printed" }, { - "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, "default": "Hello {{doc.patient}}, Your {{doc.lab_test_name}} result has been emailed to {{doc.email}}. \n{{doc.company }}. \nThank You, Good day!", "fieldname": "sms_emailed", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Result Emailed", - "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": "Result Emailed" } ], - "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-10-09 22:03:37.449441", + "links": [], + "modified": "2020-01-23 13:39:06.078738", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare 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": "Healthcare Administrator", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file From 3ca67ceebb9a0e940d247945e92dfff1d5a14873 Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 24 Jan 2020 15:52:12 +0530 Subject: [PATCH 047/929] fix: Product Page non-stock item status (#20384) --- erpnext/shopping_cart/product_info.py | 4 ++-- erpnext/utilities/product.py | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index d69b5e3a215..a7da09cb808 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -7,7 +7,7 @@ import frappe from erpnext.shopping_cart.cart import _get_cart_quotation from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings \ import get_shopping_cart_settings, show_quantity_in_website -from erpnext.utilities.product import get_price, get_qty_in_stock +from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status @frappe.whitelist(allow_guest=True) def get_product_info_for_website(item_code): @@ -31,7 +31,7 @@ def get_product_info_for_website(item_code): product_info = { "price": price, "stock_qty": stock_status.stock_qty, - "in_stock": stock_status.in_stock if stock_status.is_stock_item else 1, + "in_stock": stock_status.in_stock if stock_status.is_stock_item else get_non_stock_item_status(item_code, "website_warehouse"), "qty": 0, "uom": frappe.db.get_value("Item", item_code, "stock_uom"), "show_stock_qty": show_quantity_in_website(), diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index a2867c8806a..1c0d4c38c73 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -124,3 +124,11 @@ def get_price(item_code, price_list, customer_group, company, qty=1): price_obj["formatted_price"] = "" return price_obj + +def get_non_stock_item_status(item_code, item_warehouse_field): +#if item belongs to product bundle, check if bundle items are in stock + if frappe.db.exists("Product Bundle", item_code): + items = frappe.get_doc("Product Bundle", item_code).get_all_children() + return all([ get_qty_in_stock(d.item_code, item_warehouse_field).in_stock for d in items ]) + else: + return 1 From 2a3cd7d601382a320c12c9681d603524f5364ed8 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 24 Jan 2020 16:11:16 +0530 Subject: [PATCH 048/929] fix(Healthcare): disabled Lab Test Template shown as Enabled in List View (#20379) * fix (Healthcare): disabled Lab Test Template shown as enabled in List View * fix: remove custom disabled button and unhide disabled field --- .../lab_test_template/lab_test_template.js | 105 +- .../lab_test_template/lab_test_template.json | 1234 ++++------------- .../lab_test_template/lab_test_template.py | 114 +- .../lab_test_template_list.js | 10 +- 4 files changed, 332 insertions(+), 1131 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js index 3eb4f6abbbd..5c9bf49e608 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js @@ -3,17 +3,17 @@ frappe.ui.form.on("Lab Test Template",{ lab_test_name: function(frm) { - if(!frm.doc.lab_test_code) + if (!frm.doc.lab_test_code) frm.set_value("lab_test_code", frm.doc.lab_test_name); - if(!frm.doc.lab_test_description) + if (!frm.doc.lab_test_description) frm.set_value("lab_test_description", frm.doc.lab_test_name); }, - refresh : function(frm){ + refresh : function(frm) { // Restrict Special, Grouped type templates in Child TestGroups frm.set_query("lab_test_template", "lab_test_groups", function() { return { filters: { - lab_test_template_type:['in',['Single','Compound']] + lab_test_template_type: ['in',['Single','Compound']] } }; }); @@ -23,83 +23,44 @@ frappe.ui.form.on("Lab Test Template",{ cur_frm.cscript.custom_refresh = function(doc) { cur_frm.set_df_property("lab_test_code", "read_only", doc.__islocal ? 0 : 1); - if(!doc.__islocal) { - cur_frm.add_custom_button(__('Change Template Code'), function() { - change_template_code(cur_frm,doc); - } ); - if(doc.disabled == 1){ - cur_frm.add_custom_button(__('Enable Template'), function() { - enable_template(cur_frm); - } ); - } - else{ - cur_frm.add_custom_button(__('Disable Template'), function() { - disable_template(cur_frm); - } ); - } + if (!doc.__islocal) { + cur_frm.add_custom_button(__("Change Template Code"), function() { + change_template_code(doc); + }); } }; -var disable_template = function(frm){ - var doc = frm.doc; - frappe.call({ - method: "erpnext.healthcare.doctype.lab_test_template.lab_test_template.disable_enable_test_template", - args: {status: 1, name: doc.name, is_billable: doc.is_billable}, - callback: function(){ - cur_frm.reload_doc(); - } - }); -}; - -var enable_template = function(frm){ - var doc = frm.doc; - frappe.call({ - method: "erpnext.healthcare.doctype.lab_test_template.lab_test_template.disable_enable_test_template", - args: {status: 0, name: doc.name, is_billable: doc.is_billable}, - callback: function(){ - cur_frm.reload_doc(); - } - }); -}; - - -var change_template_code = function(frm,doc){ - var d = new frappe.ui.Dialog({ +let change_template_code = function(doc) { + let d = new frappe.ui.Dialog({ title:__("Change Template Code"), fields:[ { "fieldtype": "Data", - "label": "Test Template Code", - "fieldname": "Test Code", - reqd:1 - }, - { - "fieldtype": "Button", - "label": __("Change Code"), - click: function() { - var values = d.get_values(); - if(!values) - return; - change_test_code_from_template(values["Test Code"],doc); - d.hide(); - } + "label": "Lab Test Template Code", + "fieldname": "lab_test_code", + reqd: 1 } - ] + ], + primary_action: function() { + let values = d.get_values(); + if (values) { + frappe.call({ + "method": "erpnext.healthcare.doctype.lab_test_template.lab_test_template.change_test_code_from_template", + "args": {lab_test_code: values.lab_test_code, doc: doc}, + callback: function (data) { + frappe.set_route("Form", "Lab Test Template", data.message); + } + }); + } + d.hide(); + }, + primary_action_label: __("Change Template Code") }); d.show(); - d.set_values({ - 'Test Code': doc.lab_test_code - }); - var change_test_code_from_template = function(lab_test_code,doc){ - frappe.call({ - "method": "erpnext.healthcare.doctype.lab_test_template.lab_test_template.change_test_code_from_template", - "args": {lab_test_code: lab_test_code, doc: doc}, - callback: function (data) { - frappe.set_route("Form", "Lab Test Template", data.message); - } - }); - }; + d.set_values({ + "lab_test_code": doc.lab_test_code + }); }; frappe.ui.form.on("Lab Test Template", "lab_test_name", function(frm){ @@ -124,8 +85,8 @@ frappe.ui.form.on("Lab Test Template", "lab_test_description", function(frm){ }); frappe.ui.form.on("Lab Test Groups", "template_or_new_line", function (frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if(child.template_or_new_line =="Add new line"){ + let child = locals[cdt][cdn]; + if (child.template_or_new_line == "Add new line") { frappe.model.set_value(cdt, cdn, 'lab_test_template', ""); frappe.model.set_value(cdt, cdn, 'lab_test_description', ""); } diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json index a707560f650..fcfecba5767 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json @@ -1,1036 +1,276 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:lab_test_code", - "beta": 1, - "creation": "2016-03-29 17:35:36.761223", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 0, + "actions": [], + "allow_copy": 1, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:lab_test_code", + "beta": 1, + "creation": "2016-03-29 17:35:36.761223", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "lab_test_name", + "item", + "lab_test_code", + "lab_test_group", + "department", + "column_break_3", + "lab_test_template_type", + "disabled", + "is_billable", + "lab_test_rate", + "section_break_normal", + "lab_test_uom", + "lab_test_normal_range", + "column_break_10", + "section_break_compound", + "normal_test_templates", + "section_break_special", + "sensitivity", + "special_test_template", + "section_break_group", + "lab_test_groups", + "section_break_description", + "lab_test_description", + "sb_sample_collection", + "sample", + "sample_uom", + "sample_quantity", + "sample_collection_details", + "change_in_item" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_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": 1, - "label": "Test Name", - "length": 0, - "no_copy": 1, - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_name", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Test Name", + "no_copy": 1, + "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", - "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", - "length": 0, - "no_copy": 1, - "options": "Item", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item", + "fieldtype": "Link", + "label": "Item", + "no_copy": 1, + "options": "Item", + "read_only": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_code", - "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": "Item Code", - "length": 0, - "no_copy": 1, - "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, + "fieldname": "lab_test_code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Code", + "no_copy": 1, + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "lab_test_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Item Group", - "length": 0, - "no_copy": 0, - "options": "Item Group", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_group", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_standard_filter": 1, + "label": "Item Group", + "options": "Item Group", + "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": "department", - "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": 1, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Medical 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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "department", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_standard_filter": 1, + "label": "Department", + "options": "Medical Department", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "description": "Single for results which require only a single input, result UOM and normal value \n
\nCompound for results which require multiple input fields with corresponding event names, result UOMs and normal values\n
\nDescriptive for tests which have multiple result components and corresponding result entry fields. \n
\nGrouped for test templates which are a group of other test templates.\n
\nNo Result for tests with no results. Also, no Lab Test is created. e.g.. Sub Tests for Grouped results.", - "fieldname": "lab_test_template_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": 1, - "label": "Result Format", - "length": 0, - "no_copy": 0, - "options": "\nSingle\nCompound\nDescriptive\nGrouped\nNo Result", - "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 - }, + "description": "Single for results which require only a single input, result UOM and normal value \n
\nCompound for results which require multiple input fields with corresponding event names, result UOMs and normal values\n
\nDescriptive for tests which have multiple result components and corresponding result entry fields. \n
\nGrouped for test templates which are a group of other test templates.\n
\nNo Result for tests with no results. Also, no Lab Test is created. e.g.. Sub Tests for Grouped results.", + "fieldname": "lab_test_template_type", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Result Format", + "options": "\nSingle\nCompound\nDescriptive\nGrouped\nNo Result" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "depends_on": "eval:doc.lab_test_template_type != 'Grouped'", - "description": "If unchecked, the item wont be appear in Sales Invoice, but can be used in group test creation. ", - "fieldname": "is_billable", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is billable", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "1", + "depends_on": "eval:doc.lab_test_template_type != 'Grouped'", + "description": "If unchecked, the item wont be appear in Sales Invoice, but can be used in group test creation. ", + "fieldname": "is_billable", + "fieldtype": "Check", + "label": "Is billable", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_billable == 1", - "description": "This value is updated in the Default Sales Price List.", - "fieldname": "lab_test_rate", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Standard Selling Rate", - "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 - }, + "depends_on": "eval:doc.is_billable == 1", + "description": "This value is updated in the Default Sales Price List.", + "fieldname": "lab_test_rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Standard Selling Rate" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "eval:doc.lab_test_template_type == 'Single'", - "fieldname": "section_break_normal", - "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": "Lab Routine", - "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 - }, + "depends_on": "eval:doc.lab_test_template_type == 'Single'", + "fieldname": "section_break_normal", + "fieldtype": "Section Break", + "label": "Lab Routine" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "UOM", - "length": 0, - "no_copy": 0, - "options": "Lab Test UOM", - "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": "lab_test_uom", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "UOM", + "options": "Lab Test UOM" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_normal_range", - "fieldtype": "Long Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Normal Range", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_normal_range", + "fieldtype": "Long Text", + "ignore_xss_filter": 1, + "label": "Normal Range" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 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 - }, + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "eval:doc.lab_test_template_type == 'Compound'", - "fieldname": "section_break_compound", - "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": "Compound", - "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 - }, + "depends_on": "eval:doc.lab_test_template_type == 'Compound'", + "fieldname": "section_break_compound", + "fieldtype": "Section Break", + "label": "Compound" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "normal_test_templates", - "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, - "length": 0, - "no_copy": 0, - "options": "Normal Test 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": "normal_test_templates", + "fieldtype": "Table", + "options": "Normal Test Template" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.lab_test_template_type == 'Descriptive'", - "fieldname": "section_break_special", - "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": "Special", - "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 - }, + "depends_on": "eval:doc.lab_test_template_type == 'Descriptive'", + "fieldname": "section_break_special", + "fieldtype": "Section Break", + "label": "Special" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "sensitivity", - "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": "Sensitivity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "sensitivity", + "fieldtype": "Check", + "label": "Sensitivity" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "special_test_template", - "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, - "length": 0, - "no_copy": 0, - "options": "Special Test 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": "special_test_template", + "fieldtype": "Table", + "options": "Special Test Template" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.lab_test_template_type == 'Grouped'", - "fieldname": "section_break_group", - "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": "Group", - "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 - }, + "depends_on": "eval:doc.lab_test_template_type == 'Grouped'", + "fieldname": "section_break_group", + "fieldtype": "Section Break", + "label": "Group" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_groups", - "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": "", - "length": 0, - "no_copy": 0, - "options": "Lab Test Groups", - "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": "lab_test_groups", + "fieldtype": "Table", + "options": "Lab Test Groups" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_description", - "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_description", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_description", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 1, - "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": "lab_test_description", + "fieldtype": "Text", + "ignore_xss_filter": 1, + "label": "Description", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_sample_collection", - "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": "Sample Collection", - "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": "sb_sample_collection", + "fieldtype": "Section Break", + "label": "Sample Collection" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample", - "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": "Sample", - "length": 0, - "no_copy": 0, - "options": "Lab Test Sample", - "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": "sample", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Sample", + "options": "Lab Test Sample" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "sample.sample_uom", - "fieldname": "sample_uom", - "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": "UOM", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "fetch_from": "sample.sample_uom", + "fieldname": "sample_uom", + "fieldtype": "Data", + "label": "UOM", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "sample_quantity", - "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": "Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "sample_quantity", + "fieldtype": "Float", + "label": "Quantity" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample_collection_details", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Collection Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sample_collection_details", + "fieldtype": "Text", + "ignore_xss_filter": 1, + "label": "Collection Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "change_in_item", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Change In Item", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "change_in_item", + "fieldtype": "Check", + "hidden": 1, + "label": "Change In Item", + "no_copy": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "disabled", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "disabled", - "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": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-09-04 11:16:02.349707", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Lab Test Template", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-01-21 21:02:16.108347", + "modified_by": "ruchamahabal2@gmail.com", + "module": "Healthcare", + "name": "Lab Test Template", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 - }, + }, { - "amend": 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": "Laboratory User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Laboratory User", + "share": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "lab_test_code,lab_test_name,lab_test_template_type", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "lab_test_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "restrict_to_domain": "Healthcare", + "search_fields": "lab_test_code,lab_test_name,lab_test_template_type", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "lab_test_name", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py index 101e143c123..89f2defb14b 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py @@ -8,59 +8,76 @@ from frappe.model.document import Document from frappe import _ class LabTestTemplate(Document): + def after_insert(self): + if not self.item: + create_item_from_template(self) + + def validate(self): + self.enable_disable_item() + def on_update(self): - #Item and Price List update --> if (change_in_item) - if(self.change_in_item and self.is_billable == 1 and self.item): - updating_item(self) - item_price = item_price_exist(self) + # if change_in_item update Item and Price List + if self.change_in_item and self.is_billable and self.item: + self.update_item() + item_price = self.item_price_exists() if not item_price: - if(self.lab_test_rate != 0.0): + if self.lab_test_rate != 0.0: price_list_name = frappe.db.get_value("Price List", {"selling": 1}) - if(self.lab_test_rate): + if self.lab_test_rate: make_item_price(self.lab_test_code, price_list_name, self.lab_test_rate) else: make_item_price(self.lab_test_code, price_list_name, 0.0) else: frappe.db.set_value("Item Price", item_price, "price_list_rate", self.lab_test_rate) - frappe.db.set_value(self.doctype,self.name,"change_in_item",0) - elif(self.is_billable == 0 and self.item): - frappe.db.set_value("Item",self.item,"disabled",1) + frappe.db.set_value(self.doctype, self.name, "change_in_item", 0) + + elif not self.is_billable and self.item: + frappe.db.set_value("Item", self.item, "disabled", 1) + self.reload() - def after_insert(self): - if not self.item: - create_item_from_template(self) - - #Call before delete the template def on_trash(self): - # remove template refernce from item and disable item - if(self.item): + # remove template reference from item and disable item + if self.item: try: - frappe.delete_doc("Item",self.item, force=True) + frappe.delete_doc("Item", self.item) except Exception: - frappe.throw(_("""Not permitted. Please disable the Test Template""")) + frappe.throw(_("Not permitted. Please disable the Lab Test Template")) -def item_price_exist(doc): - item_price = frappe.db.exists({ - "doctype": "Item Price", - "item_code": doc.lab_test_code}) - if(item_price): - return item_price[0][0] - else: - return False + def enable_disable_item(self): + if self.is_billable: + if self.disabled: + frappe.db.set_value('Item', self.item, 'disabled', 1) + else: + frappe.db.set_value('Item', self.item, 'disabled', 0) + + def update_item(self): + item = frappe.get_doc("Item", self.item) + if item: + item.update({ + "item_name": self.lab_test_name, + "item_group": self.lab_test_group, + "disabled": 0, + "standard_rate": self.lab_test_rate, + "description": self.lab_test_description + }) + item.save() + + def item_price_exists(self): + item_price = frappe.db.exists({"doctype": "Item Price", "item_code": self.lab_test_code}) + if item_price: + return item_price[0][0] + else: + return False -def updating_item(self): - frappe.db.sql("""update `tabItem` set item_name=%s, item_group=%s, disabled=0, standard_rate=%s, - description=%s, modified=NOW() where item_code=%s""", - (self.lab_test_name, self.lab_test_group , self.lab_test_rate, self.lab_test_description, self.item)) def create_item_from_template(doc): - if(doc.is_billable == 1): + if doc.is_billable: disabled = 0 else: disabled = 1 - #insert item + # insert item item = frappe.get_doc({ "doctype": "Item", "item_code": doc.lab_test_code, @@ -77,9 +94,9 @@ def create_item_from_template(doc): "stock_uom": "Unit" }).insert(ignore_permissions=True) - #insert item price - #get item price list to insert item price - if(doc.lab_test_rate != 0.0): + # insert item price + # get item price list to insert item price + if doc.lab_test_rate != 0.0: price_list_name = frappe.db.get_value("Price List", {"selling": 1}) if(doc.lab_test_rate): make_item_price(item.name, price_list_name, doc.lab_test_rate) @@ -88,10 +105,10 @@ def create_item_from_template(doc): make_item_price(item.name, price_list_name, 0.0) item.standard_rate = 0.0 item.save(ignore_permissions = True) - #Set item to the template + # Set item in the template frappe.db.set_value("Lab Test Template", doc.name, "item", item.name) - doc.reload() #refresh the doc after insert. + doc.reload() def make_item_price(item, price_list_name, item_price): frappe.get_doc({ @@ -103,22 +120,13 @@ def make_item_price(item, price_list_name, item_price): @frappe.whitelist() def change_test_code_from_template(lab_test_code, doc): - args = json.loads(doc) - doc = frappe._dict(args) + doc = frappe._dict(json.loads(doc)) - item_exist = frappe.db.exists({ - "doctype": "Item", - "item_code": lab_test_code}) - if(item_exist): - frappe.throw(_("Code {0} already exist").format(lab_test_code)) + if frappe.db.exists({ "doctype": "Item", "item_code": lab_test_code}): + frappe.throw(_("Lab Test Item {0} already exist").format(lab_test_code)) else: - frappe.rename_doc("Item", doc.name, lab_test_code, ignore_permissions = True) - frappe.db.set_value("Lab Test Template",doc.name,"lab_test_code",lab_test_code) - frappe.rename_doc("Lab Test Template", doc.name, lab_test_code, ignore_permissions = True) + rename_doc("Item", doc.name, lab_test_code, ignore_permissions=True) + frappe.db.set_value("Lab Test Template", doc.name, "lab_test_code", lab_test_code) + frappe.db.set_value("Lab Test Template", doc.name, "lab_test_name", lab_test_code) + rename_doc("Lab Test Template", doc.name, lab_test_code, ignore_permissions=True) return lab_test_code - -@frappe.whitelist() -def disable_enable_test_template(status, name, is_billable): - frappe.db.set_value("Lab Test Template",name,"disabled",status) - if(is_billable == 1): - frappe.db.set_value("Item",name,"disabled",status) diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js index a16a72f55b2..a86075fc943 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js @@ -3,13 +3,5 @@ */ frappe.listview_settings['Lab Test Template'] = { add_fields: ["lab_test_name", "lab_test_code", "lab_test_rate"], - filters:[["disabled","=",0]], - /* get_indicator: function(doc) { - if(doc.disabled==1){ - return [__("Disabled"), "red", "disabled,=,Disabled"]; - } - if(doc.disabled==0){ - return [__("Enabled"), "green", "disabled,=,0"]; - } - } */ + filters: [["disabled", "=", 0]] }; From 9e3dc25b1cbb47257bc73393edbd8783ce2c2e90 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Fri, 24 Jan 2020 16:14:38 +0530 Subject: [PATCH 049/929] fix: Duplicate Attendance Due Wrong Date format (#20389) --- erpnext/hr/doctype/attendance/attendance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index b8081128ff9..e612c3d7572 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -13,7 +13,7 @@ class Attendance(Document): def validate_duplicate_record(self): res = frappe.db.sql("""select name from `tabAttendance` where employee = %s and attendance_date = %s and name != %s and docstatus != 2""", - (self.employee, self.attendance_date, self.name)) + (self.employee, getdate(self.attendance_date), self.name)) if res: frappe.throw(_("Attendance for employee {0} is already marked").format(self.employee)) From 92c999afcbfe0c0e2e940a1f53b00369deb9b6df Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 24 Jan 2020 16:23:05 +0530 Subject: [PATCH 050/929] fix: wrong outstanding invoices fetched against employee (#20374) --- erpnext/accounts/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index e01d6d5dc7f..5ad8cb50d5f 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -640,8 +640,9 @@ def get_outstanding_invoices(party_type, party, account, condition=None, filters precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2 if account: - root_type = frappe.get_cached_value("Account", account, "root_type") + root_type, account_type = frappe.get_cached_value("Account", account, ["root_type", "account_type"]) party_account_type = "Receivable" if root_type == "Asset" else "Payable" + party_account_type = account_type or party_account_type else: party_account_type = erpnext.get_party_account_type(party_type) From 25a7330db0a2072aa890ecd885b17c947ca28833 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 24 Jan 2020 17:36:59 +0530 Subject: [PATCH 051/929] fix: change primary addr fieldtype to Text --- erpnext/selling/doctype/customer/customer.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index c2c8c1971a9..250973b7497 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -322,8 +322,9 @@ }, { "fieldname": "primary_address", - "fieldtype": "Read Only", - "label": "Primary Address" + "fieldtype": "Text", + "label": "Primary Address", + "read_only": 1 }, { "collapsible": 1, @@ -469,7 +470,7 @@ "icon": "fa fa-user", "idx": 363, "image_field": "image", - "modified": "2019-09-06 12:40:31.801424", + "modified": "2020-01-24 15:06:03.483799", "modified_by": "Administrator", "module": "Selling", "name": "Customer", From 8d9145aaac9e278b9c1e641180c55220b5aabde1 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 27 Jan 2020 08:48:04 +0530 Subject: [PATCH 052/929] fix: Code cleanup and commonification --- .../item_wise_sales_register.js | 6 +- .../item_wise_sales_register.py | 298 +++++++++++------- 2 files changed, 182 insertions(+), 122 deletions(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js index 765d761fc4c..8a9c76f26f8 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js @@ -51,17 +51,15 @@ frappe.query_reports["Item-wise Sales Register"] = { "label": __("Group By"), "fieldname": "group_by", "fieldtype": "Select", - "options": ["Customer", "Item Group", "Item", "Invoice"] + "options": ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"] } ], "formatter": function(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (data && data.bold) { value = value.bold(); } - return value; - }, + } } diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 65a4b24a595..57a99ef76a9 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _ -from frappe.utils import flt +from frappe.utils import flt, cstr from frappe.model.meta import get_field_precision from frappe.utils.xlsxutils import handle_html from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments @@ -23,23 +23,16 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum if item_list: itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency) - columns.append({ - 'fieldname': 'currency', - 'label': _('Currency'), - 'fieldtype': 'Currency', - 'width': 80, - 'hidden': 1 - }) - mode_of_payments = get_mode_of_payments(set([d.parent for d in item_list])) so_dn_map = get_delivery_notes_against_sales_order(item_list) - if filters.get('group_by'): - grand_total = get_grand_total(filters) - data = [] + total_row_map = {} prev_group_by_value = '' + if filters.get('group_by'): + grand_total = get_grand_total(filters, 'Sales Invoice') + for d in item_list: delivery_note = None if d.delivery_note: @@ -50,35 +43,6 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum if not delivery_note and d.update_stock: delivery_note = d.parent - - if filters.get('group_by'): - if filters.get('group_by') == 'Item': - group_by_field = 'item_code' - subtotal_display_field = 'customer' - elif filters.get('group_by') == 'Invoice': - group_by_field = 'parent' - subtotal_display_field = 'item_code' - else: - group_by_field = frappe.scrub(filters.get('group_by')) - subtotal_display_field = 'item_code' - - if prev_group_by_value != d.get(group_by_field): - if prev_group_by_value: - total_row['percent_gt'] = flt(total_row['total']/grand_total * 100) - data.append(total_row) - data.append({}) - - prev_group_by_value = d.get(group_by_field) - - total_row = { - subtotal_display_field: d.get(group_by_field), - 'stock_qty': 0.0, - 'amount': 0.0, - 'bold': 1, - 'total_tax': 0.0, - 'total': 0.0 - } - row = { 'item_code': d.item_code, 'item_name': d.item_name, @@ -87,7 +51,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum 'invoice': d.parent, 'posting_date': d.posting_date, 'customer': d.customer, - 'customer_name': d.customer_name + 'customer_name': d.customer_name, + 'customer_group': d.customer_group, } if additional_query_columns: @@ -97,7 +62,6 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum }) row.update({ - 'customer_group': d.customer_group, 'debit_to': d.debit_to, 'mode_of_payment': ", ".join(mode_of_payments.get(d.parent, [])), 'territory': d.territory, @@ -138,42 +102,56 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum }) if filters.get('group_by'): - total_row['stock_qty'] += row['stock_qty'] - total_row['amount'] += row['amount'] - total_row['total_tax'] += row['total_tax'] - total_row['total'] += row['total'] + row.update({'percent_gt': flt(row['total']/grand_total) * 100}) + group_by_field, subtotal_display_field = get_group_by_and_display_fields(filters) + data, prev_group_by_value = add_total_row(data, filters, prev_group_by_value, d, total_row_map, + group_by_field, subtotal_display_field, grand_total) + update_total_row(row, total_row_map, d.get(group_by_field, ''), tax_columns) data.append(row) if filters.get('group_by'): - total_row['percent_gt'] = frappe.bold(total_row['total']/grand_total * 100) + total_row = total_row_map.get(prev_group_by_value or d.get('item_name')) + total_row['percent_gt'] = flt(total_row['total']/grand_total * 100) data.append(total_row) data.append({}) return columns, data def get_columns(additional_table_columns, filters): - columns = [ - { - 'label': _('Item Code'), - 'fieldname': 'item_code', - 'fieldtype': 'Link', - 'options': 'Item', - 'width': 120 - }, - { - 'label': _('Item Name'), - 'fieldname': 'item_name', - 'fieldtype': 'Data', - 'width': 120 - }, - { - 'label': _('Item Group'), - 'fieldname': 'item_group', - 'fieldtype': 'Link', - 'options': 'Item Group', - 'width': 120 - }, + columns = [] + + if filters.get('group_by') != ('Item'): + columns.extend( + [ + { + 'label': _('Item Code'), + 'fieldname': 'item_code', + 'fieldtype': 'Link', + 'options': 'Item', + 'width': 120 + }, + { + 'label': _('Item Name'), + 'fieldname': 'item_name', + 'fieldtype': 'Data', + 'width': 120 + } + ] + ) + + if filters.get('group_by') not in ('Item', 'Item Group'): + columns.extend([ + { + 'label': _('Item Group'), + 'fieldname': 'item_group', + 'fieldtype': 'Link', + 'options': 'Item Group', + 'width': 120 + } + ]) + + columns.extend([ { 'label': _('Description'), 'fieldname': 'description', @@ -192,33 +170,41 @@ def get_columns(additional_table_columns, filters): 'fieldname': 'posting_date', 'fieldtype': 'Date', 'width': 120 - }, - { - 'label': _('Customer'), - 'fieldname': 'customer', - 'fieldtype': 'Link', - 'options': 'Customer', - 'width': 120 - }, - { - 'label': _('Customer Name'), - 'fieldname': 'customer_name', - 'fieldtype': 'Data', - 'width': 120 } - ] + ]) + + if filters.get('group_by') != 'Customer': + columns.extend([ + { + 'label': _('Customer Group'), + 'fieldname': 'customer_group', + 'fieldtype': 'Link', + 'options': 'Customer Group', + 'width': 120 + } + ]) + + if filters.get('group_by') not in ('Customer', 'Customer Group'): + columns.extend([ + { + 'label': _('Customer'), + 'fieldname': 'customer', + 'fieldtype': 'Link', + 'options': 'Customer', + 'width': 120 + }, + { + 'label': _('Customer Name'), + 'fieldname': 'customer_name', + 'fieldtype': 'Data', + 'width': 120 + } + ]) if additional_table_columns: columns += additional_table_columns columns += [ - { - 'label': _('Customer Group'), - 'fieldname': 'customer_group', - 'fieldtype': 'Link', - 'options': 'Customer Group', - 'width': 120 - }, { 'label': _('Receivable Account'), 'fieldname': 'debit_to', @@ -231,14 +217,22 @@ def get_columns(additional_table_columns, filters): 'fieldname': 'mode_of_payment', 'fieldtype': 'Data', 'width': 120 - }, - { - 'label': _("Territory"), - 'fieldname': 'territory', - 'fieldtype': 'Link', - 'options': 'Territory', - 'width': 80 - }, + } + ] + + if filters.get('group_by') != 'Terriotory': + columns.extend([ + { + 'label': _("Territory"), + 'fieldname': 'territory', + 'fieldtype': 'Link', + 'options': 'Territory', + 'width': 80 + } + ]) + + + columns += [ { 'label': _('Project'), 'fieldname': 'project', @@ -307,6 +301,13 @@ def get_columns(additional_table_columns, filters): 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 + }, + { + 'fieldname': 'currency', + 'label': _('Currency'), + 'fieldtype': 'Currency', + 'width': 80, + 'hidden': 1 } ] @@ -337,35 +338,34 @@ def get_conditions(filters): and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)""" if filters.get("warehouse"): - conditions += """ and exists(select name from `tabSales Invoice Item` - where parent=`tabSales Invoice`.name - and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s)""" + conditions += """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s""" if filters.get("brand"): - conditions += """ and exists(select name from `tabSales Invoice Item` - where parent=`tabSales Invoice`.name - and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s)""" + conditions += """and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s""" if filters.get("item_group"): - conditions += """ and exists(select name from `tabSales Invoice Item` - where parent=`tabSales Invoice`.name - and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)""" + conditions += """and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s""" if not filters.get("group_by"): conditions += "ORDER BY `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_group desc" + else: + conditions += get_group_by_conditions(filters, 'Sales Invoice') + + return conditions + +def get_group_by_conditions(filters, doctype): if filters.get("group_by"): if filters.get("group_by") == 'Invoice': - conditions += " ORDER BY `tabSales Invoice Item`.parent desc" + return "ORDER BY `tab{0} Item`.parent desc".format(doctype) elif filters.get("group_by") == 'Item': - conditions += " ORDER BY `tabSales Invoice Item`.item_code" + return "ORDER BY `tab{0} Item`.`item_code`".format(doctype) elif filters.get("group_by") == 'Item Group': - conditions += " ORDER BY `tabSales Invoice Item`.`item_group`" - elif filters.get("group_by") == 'Customer': - conditions += " ORDER BY `tabSales Invoice`.customer" + return "ORDER BY `tab{0} Item`.{1}".format(doctype, frappe.scrub(filters.get('group_by'))) + elif filters.get("group_by") in ('Customer', 'Customer Group', 'Territory', 'Supplier'): + return "ORDER BY `tab{0}`.{1}".format(doctype, frappe.scrub(filters.get('group_by'))) - return conditions def get_items(filters, additional_query_columns): conditions = get_conditions(filters) @@ -413,14 +413,14 @@ def get_delivery_notes_against_sales_order(item_list): return so_dn_map -def get_grand_total(filters): +def get_grand_total(filters, doctype): return frappe.db.sql(""" SELECT - SUM(`tabSales Invoice`.grand_total) - FROM `tabSales Invoice` - WHERE `tabSales Invoice`.docstatus = 1 + SUM(`tab{0}`.base_grand_total) + FROM `tab{0}` + WHERE `tab{0}`.docstatus = 1 and posting_date between %s and %s - """, (filters.get('from_date'), filters.get('to_date')))[0][0] + """.format(doctype), (filters.get('from_date'), filters.get('to_date')))[0][0] def get_deducted_taxes(): return frappe.db.sql_list("select name from `tabPurchase Taxes and Charges` where add_deduct_tax = 'Deduct'") @@ -542,3 +542,65 @@ def get_tax_accounts(item_list, columns, company_currency, ] return itemised_tax, tax_columns + +def add_total_row(data, filters, prev_group_by_value, item, total_row_map, + group_by_field, subtotal_display_field, grand_total): + if prev_group_by_value != item.get(group_by_field, ''): + if prev_group_by_value: + total_row = total_row_map.get(prev_group_by_value) + total_row['percent_gt'] = flt(total_row['total']/grand_total * 100) + data.append(total_row) + data.append({}) + + prev_group_by_value = item.get(group_by_field, '') + + total_row_map.setdefault(item.get(group_by_field, ''), { + subtotal_display_field: get_display_value(filters, group_by_field, item), + 'stock_qty': 0.0, + 'amount': 0.0, + 'bold': 1, + 'total_tax': 0.0, + 'total': 0.0 + }) + + return data, prev_group_by_value + +def get_display_value(filters, group_by_field, item): + if filters.get('group_by') == 'Item': + return "Item: " + cstr(item.get('item_code')) \ + + "

" + "Item Name: " + cstr(item.get('item_name')) + elif filters.get('group_by') in ('Customer', 'Supplier'): + return filters.get('group_by')+": " + cstr(item.get(frappe.scrub(filters.get('group_by')))) \ + + "

" + filters.get('group_by') + " Name: " + item.get(frappe.scrub(filters.get('group_by'))+'_name', '') + else: + return item.get(group_by_field) + +def get_group_by_and_display_fields(filters): + if filters.get('group_by') == 'Item': + group_by_field = 'item_code' + subtotal_display_field = 'invoice' + elif filters.get('group_by') == 'Invoice': + group_by_field = 'parent' + subtotal_display_field = 'item_code' + else: + group_by_field = frappe.scrub(filters.get('group_by')) + subtotal_display_field = 'item_code' + + return group_by_field, subtotal_display_field + +def update_total_row(item, total_row_map, group_by_value, tax_columns): + total_row = total_row_map.get(group_by_value) + total_row['stock_qty'] += item['stock_qty'] + total_row['amount'] += item['amount'] + total_row['total_tax'] += item['total_tax'] + total_row['total'] += item['total'] + + for tax in tax_columns: + total_row.setdefault(frappe.scrub(tax + ' Amount'), 0.0) + total_row.setdefault(frappe.scrub(tax + ' Rate'), 0.0) + total_row[frappe.scrub(tax + ' Rate')] += flt(item[frappe.scrub(tax + ' Rate')]) + total_row[frappe.scrub(tax + ' Amount')] += flt(item[frappe.scrub(tax + ' Amount')]) + + + + From 6f27e9798020f71bc8378243ba2d2446c360dfcc Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 27 Jan 2020 08:48:38 +0530 Subject: [PATCH 053/929] fix: Add group by functionality to purchase register --- .../item_wise_purchase_register.js | 16 +- .../item_wise_purchase_register.py | 258 +++++++++++++++--- 2 files changed, 241 insertions(+), 33 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js index e936a173734..f88906a0f1f 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js @@ -34,6 +34,20 @@ frappe.query_reports["Item-wise Purchase Register"] = { "label": __("Mode of Payment"), "fieldtype": "Link", "options": "Mode of Payment" + }, + { + "label": __("Group By"), + "fieldname": "group_by", + "fieldtype": "Select", + "options": ["Supplier", "Item Group", "Item", "Invoice"] } - ] + ], + "formatter": function(value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + if (data && data.bold) { + value = value.bold(); + + } + return value; + } } diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 380b208548c..3a5a4548f5a 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -5,7 +5,9 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _ from frappe.utils import flt -from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import get_tax_accounts +from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (get_tax_accounts, + get_grand_total, add_total_row, get_display_value, get_group_by_and_display_fields, update_total_row, + get_group_by_conditions) def execute(filters=None): return _execute(filters) @@ -13,7 +15,7 @@ def execute(filters=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): if not filters: filters = {} filters.update({"from_date": filters.get("date_range")[0], "to_date": filters.get("date_range")[1]}) - columns = get_columns(additional_table_columns) + columns = get_columns(additional_table_columns, filters) company_currency = erpnext.get_company_currency(filters.company) @@ -23,16 +25,15 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency, doctype="Purchase Invoice", tax_doctype="Purchase Taxes and Charges") - columns.append({ - "fieldname": "currency", - "label": _("Currency"), - "fieldtype": "Data", - "width": 80 - }) - po_pr_map = get_purchase_receipts_against_purchase_order(item_list) data = [] + total_row_map = {} + prev_group_by_value = '' + + if filters.get('group_by'): + grand_total = get_grand_total(filters, 'Purchase Invoice') + for d in item_list: if not d.stock_qty: continue @@ -44,51 +45,240 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum purchase_receipt = ", ".join(po_pr_map.get(d.po_detail, [])) expense_account = d.expense_account or aii_account_map.get(d.company) - row = [d.item_code, d.item_name, d.item_group, d.description, d.parent, d.posting_date, d.supplier, - d.supplier_name] + + row = { + 'item_code': d.item_code, + 'item_name': d.item_name, + 'item_group': d.item_group, + 'description': d.description, + 'invoice': d.parent, + 'posting_date': d.posting_date, + 'customer': d.supplier, + 'customer_name': d.supplier_name + } if additional_query_columns: for col in additional_query_columns: - row.append(d.get(col)) + row.update({ + col: d.get(col) + }) - row += [ - d.credit_to, d.mode_of_payment, d.project, d.company, d.purchase_order, - purchase_receipt, expense_account, d.stock_qty, d.stock_uom, d.base_net_amount / d.stock_qty, d.base_net_amount - ] + row.update({ + 'credit_to': d.credit_to, + 'mode_of_payment': d.mode_of_payment, + 'project': d.project, + 'company': d.company, + 'purchase_order': d.purchase_order, + 'purchase_receipt': d.purchase_receipt, + 'expense_account': expense_account, + 'stock_qty': d.stock_qty, + 'stock_uom': d.stock_uom, + 'rate': d.base_net_amount / d.stock_qty, + 'amount': d.base_net_amount + }) total_tax = 0 for tax in tax_columns: item_tax = itemised_tax.get(d.name, {}).get(tax, {}) - row += [item_tax.get("tax_rate", 0), item_tax.get("tax_amount", 0)] + row.update({ + frappe.scrub(tax + ' Rate'): item_tax.get("tax_rate", 0), + frappe.scrub(tax + ' Amount'): item_tax.get("tax_amount", 0), + }) total_tax += flt(item_tax.get("tax_amount")) - row += [total_tax, d.base_net_amount + total_tax, company_currency] + row.update({ + 'total_tax': total_tax, + 'total': d.base_net_amount + total_tax, + 'currency': company_currency + }) + + if filters.get('group_by'): + row.update({'percent_gt': flt(row['total']/grand_total) * 100}) + group_by_field, subtotal_display_field = get_group_by_and_display_fields(filters) + data, prev_group_by_value = add_total_row(data, filters, prev_group_by_value, d, total_row_map, + group_by_field, subtotal_display_field, grand_total) + update_total_row(row, total_row_map, d.get(group_by_field, ''), tax_columns) data.append(row) + if filters.get('group_by'): + total_row = total_row_map.get(prev_group_by_value or d.get('item_name')) + total_row['percent_gt'] = flt(total_row['total']/grand_total * 100) + data.append(total_row) + data.append({}) + return columns, data -def get_columns(additional_table_columns): - columns = [ - _("Item Code") + ":Link/Item:120", _("Item Name") + "::120", - _("Item Group") + ":Link/Item Group:100", "Description::150", _("Invoice") + ":Link/Purchase Invoice:120", - _("Posting Date") + ":Date:80", _("Supplier") + ":Link/Supplier:120", - "Supplier Name::120" - ] +def get_columns(additional_table_columns, filters): + + columns = [] + + if filters.get('group_by') != ('Item'): + columns.extend( + [ + { + 'label': _('Item Code'), + 'fieldname': 'item_code', + 'fieldtype': 'Link', + 'options': 'Item', + 'width': 120 + }, + { + 'label': _('Item Name'), + 'fieldname': 'item_name', + 'fieldtype': 'Data', + 'width': 120 + } + ] + ) + + if filters.get('group_by') not in ('Item', 'Item Group'): + columns.extend([ + { + 'label': _('Item Group'), + 'fieldname': 'item_group', + 'fieldtype': 'Link', + 'options': 'Item Group', + 'width': 120 + } + ]) + + columns.extend([ + { + 'label': _('Description'), + 'fieldname': 'description', + 'fieldtype': 'Data', + 'width': 150 + }, + { + 'label': _('Invoice'), + 'fieldname': 'invoice', + 'fieldtype': 'Link', + 'options': 'Purchase Invoice', + 'width': 120 + }, + { + 'label': _('Posting Date'), + 'fieldname': 'posting_date', + 'fieldtype': 'Date', + 'width': 120 + } + ]) + + if filters.get('group_by') != 'Supplier': + columns.extend([ + { + 'label': _('Supplier'), + 'fieldname': 'supplier', + 'fieldtype': 'Link', + 'options': 'Supplier', + 'width': 120 + }, + { + 'label': _('Supplier Name'), + 'fieldname': 'supplier_name', + 'fieldtype': 'Data', + 'width': 120 + } + ]) if additional_table_columns: columns += additional_table_columns columns += [ - "Payable Account:Link/Account:120", - _("Mode of Payment") + ":Link/Mode of Payment:80", _("Project") + ":Link/Project:80", - _("Company") + ":Link/Company:100", _("Purchase Order") + ":Link/Purchase Order:100", - _("Purchase Receipt") + ":Link/Purchase Receipt:100", _("Expense Account") + ":Link/Account:140", - _("Stock Qty") + ":Float:120", _("Stock UOM") + "::100", - _("Rate") + ":Currency/currency:120", _("Amount") + ":Currency/currency:120" + { + 'label': _('Payable Account'), + 'fieldname': 'credit_to', + 'fieldtype': 'Link', + 'options': 'Account', + 'width': 80 + }, + { + 'label': _('Mode Of Payment'), + 'fieldname': 'mode_of_payment', + 'fieldtype': 'Data', + 'width': 120 + }, + { + 'label': _('Project'), + 'fieldname': 'project', + 'fieldtype': 'Link', + 'options': 'Project', + 'width': 80 + }, + { + 'label': _('Company'), + 'fieldname': 'company', + 'fieldtype': 'Link', + 'options': 'Company', + 'width': 80 + }, + { + 'label': _('Purchase Order'), + 'fieldname': 'purchase_order', + 'fieldtype': 'Link', + 'options': 'Purchase Order', + 'width': 100 + }, + { + 'label': _("Purchase Receipt"), + 'fieldname': 'Purchase Receipt', + 'fieldtype': 'Link', + 'options': 'Purchase Receipt', + 'width': 100 + }, + { + 'label': _('Expense Account'), + 'fieldname': 'expense_account', + 'fieldtype': 'Link', + 'options': 'Account', + 'width': 100 + }, + { + 'label': _('Stock Qty'), + 'fieldname': 'stock_qty', + 'fieldtype': 'Float', + 'width': 100 + }, + { + 'label': _('Stock UOM'), + 'fieldname': 'stock_uom', + 'fieldtype': 'Link', + 'options': 'UOM', + 'width': 100 + }, + { + 'label': _('Rate'), + 'fieldname': 'rate', + 'fieldtype': 'Float', + 'options': 'currency', + 'width': 100 + }, + { + 'label': _('Amount'), + 'fieldname': 'amount', + 'fieldtype': 'Currency', + 'options': 'currency', + 'width': 100 + }, + { + 'fieldname': 'currency', + 'label': _('Currency'), + 'fieldtype': 'Currency', + 'width': 80, + 'hidden': 1 + } ] + if filters.get('group_by'): + columns.append({ + 'label': _('% Of Grand Total'), + 'fieldname': 'percent_gt', + 'fieldtype': 'Float', + 'width': 80 + }) + return columns def get_conditions(filters): @@ -103,6 +293,11 @@ def get_conditions(filters): if filters.get(opts[0]): conditions += opts[1] + if not filters.get("group_by"): + conditions += "ORDER BY `tabPurchase Invoice`.posting_date desc, `tabPurchase Invoice Item`.item_code desc" + else: + conditions += get_group_by_conditions(filters, 'Purchase Invoice') + return conditions def get_items(filters, additional_query_columns): @@ -129,7 +324,6 @@ def get_items(filters, additional_query_columns): from `tabPurchase Invoice`, `tabPurchase Invoice Item` where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and `tabPurchase Invoice`.docstatus = 1 %s %s - order by `tabPurchase Invoice`.posting_date desc, `tabPurchase Invoice Item`.item_code desc """.format(additional_query_columns) % (conditions, match_conditions), filters, as_dict=1) def get_aii_accounts(): From 612e3b77fdf8da501739d33aacd67a8da316b7fe Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 27 Jan 2020 14:23:05 +0530 Subject: [PATCH 054/929] fix: incorrect status for fraction of outstanding amount (#20370) --- .../doctype/sales_invoice/sales_invoice.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 24024dc3100..a421cc4d6dc 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1232,24 +1232,27 @@ class SalesInvoice(SellingController): self.status = 'Draft' return + precision = self.precision("outstanding_amount") + outstanding_amount = flt(self.outstanding_amount, precision) + if not status: if self.docstatus == 2: status = "Cancelled" elif self.docstatus == 1: - if flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + if outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': self.status = "Overdue and Discounted" - elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()): + elif outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()): self.status = "Overdue" - elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + elif outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': self.status = "Unpaid and Discounted" - elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()): + elif outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()): self.status = "Unpaid" #Check if outstanding amount is 0 due to credit note issued against invoice - elif flt(self.outstanding_amount) <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): + elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): self.status = "Credit Note Issued" elif self.is_return == 1: self.status = "Return" - elif flt(self.outstanding_amount)<=0: + elif outstanding_amount <=0: self.status = "Paid" else: self.status = "Submitted" From 148951e6f30d7858bbed043fe3df08eafbee094e Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 27 Jan 2020 15:18:45 +0530 Subject: [PATCH 055/929] fix: Finance book filtering in financial statements (#20411) * fix: Finance book filtering in financial statements * fix: Use IS NULL instead of ifnull --- erpnext/accounts/report/balance_sheet/balance_sheet.js | 3 ++- erpnext/accounts/report/cash_flow/cash_flow.js | 3 ++- erpnext/accounts/report/cash_flow/cash_flow.py | 4 ++-- .../consolidated_financial_statement.js | 3 ++- .../consolidated_financial_statement.py | 4 ++-- erpnext/accounts/report/financial_statements.py | 4 ++-- erpnext/accounts/report/general_ledger/general_ledger.js | 3 ++- erpnext/accounts/report/general_ledger/general_ledger.py | 2 +- .../profit_and_loss_statement/profit_and_loss_statement.js | 3 ++- erpnext/accounts/report/trial_balance/trial_balance.js | 3 ++- erpnext/accounts/report/trial_balance/trial_balance.py | 4 ++-- 11 files changed, 21 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index 8c11514aa64..c4c24c0bab3 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -14,6 +14,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), - "fieldtype": "Check" + "fieldtype": "Check", + "default": 1 }); }); diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index 03940f4b246..89244c3781a 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -20,7 +20,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), - "fieldtype": "Check" + "fieldtype": "Check", + "default": 1 } ); }); \ No newline at end of file diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index 98c25b7514a..0b124774479 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -130,11 +130,11 @@ def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters = frappe._dict(filters) if filters.finance_book: - cond = " and finance_book = %s" %(frappe.db.escape(filters.finance_book)) + cond = " AND (finance_book in (%s, '') OR finance_book IS NULL)" %(frappe.db.escape(filters.finance_book)) if filters.include_default_book_entries: company_fb = frappe.db.get_value("Company", company, 'default_finance_book') - cond = """ and finance_book in (%s, %s) + cond = """ AND (finance_book in (%s, %s, '') OR finance_book IS NULL) """ %(frappe.db.escape(filters.finance_book), frappe.db.escape(company_fb)) gl_sum = frappe.db.sql_list(""" diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index e69a993e8ce..48a030a99ed 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -58,7 +58,8 @@ frappe.query_reports["Consolidated Financial Statement"] = { { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), - "fieldtype": "Check" + "fieldtype": "Check", + "default": 1 } ] } diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 418a23c2d7e..e9eb819cc64 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -389,9 +389,9 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): if filters.get("finance_book"): if filters.get("include_default_book_entries"): - additional_conditions.append("finance_book in (%(finance_book)s, %(company_fb)s)") + additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") else: - additional_conditions.append("finance_book in (%(finance_book)s)") + additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else "" diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 40d5682726d..32d9075f443 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -408,9 +408,9 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): if filters.get("finance_book"): if filters.get("include_default_book_entries"): - additional_conditions.append("finance_book in (%(finance_book)s, %(company_fb)s)") + additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") else: - additional_conditions.append("finance_book in (%(finance_book)s)") + additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") if accounting_dimensions: for dimension in accounting_dimensions: diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 4a287060b39..ac49d373d47 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -154,7 +154,8 @@ frappe.query_reports["General Ledger"] = { { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), - "fieldtype": "Check" + "fieldtype": "Check", + "default": 1 } ] } diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index b32a54f5cc1..0939354efa7 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -184,7 +184,7 @@ def get_conditions(filters): if filters.get("finance_book"): if filters.get("include_default_book_entries"): - conditions.append("finance_book in (%(finance_book)s, %(company_fb)s)") + conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") else: conditions.append("finance_book in (%(finance_book)s)") diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index a8362bf62dd..baa0bda7005 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -23,7 +23,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), - "fieldtype": "Check" + "fieldtype": "Check", + "default": 1 } ); }); diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index f15b5b1a19e..622bab6946f 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -85,7 +85,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), - "fieldtype": "Check" + "fieldtype": "Check", + "default": 1 } ], "formatter": erpnext.financial_statements.formatter, diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index faeee0f76aa..69285cc9634 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -103,9 +103,9 @@ def get_rootwise_opening_balances(filters, report_type): where lft >= %s and rgt <= %s)""" % (lft, rgt) if filters.finance_book: - fb_conditions = " and finance_book = %(finance_book)s" + fb_conditions = " AND finance_book = %(finance_book)s" if filters.include_default_book_entries: - fb_conditions = " and (finance_book in (%(finance_book)s, %(company_fb)s))" + fb_conditions = " AND (finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)" additional_conditions += fb_conditions From eec0ee83e228f20db1694098589fb8469d227971 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 27 Jan 2020 15:48:01 +0530 Subject: [PATCH 056/929] fix: Changes in total row display --- .../item_wise_sales_register.py | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 57a99ef76a9..cf48ad4701d 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -355,17 +355,14 @@ def get_conditions(filters): return conditions def get_group_by_conditions(filters, doctype): - - if filters.get("group_by"): - if filters.get("group_by") == 'Invoice': - return "ORDER BY `tab{0} Item`.parent desc".format(doctype) - elif filters.get("group_by") == 'Item': - return "ORDER BY `tab{0} Item`.`item_code`".format(doctype) - elif filters.get("group_by") == 'Item Group': - return "ORDER BY `tab{0} Item`.{1}".format(doctype, frappe.scrub(filters.get('group_by'))) - elif filters.get("group_by") in ('Customer', 'Customer Group', 'Territory', 'Supplier'): - return "ORDER BY `tab{0}`.{1}".format(doctype, frappe.scrub(filters.get('group_by'))) - + if filters.get("group_by") == 'Invoice': + return "ORDER BY `tab{0} Item`.parent desc".format(doctype) + elif filters.get("group_by") == 'Item': + return "ORDER BY `tab{0} Item`.`item_code`".format(doctype) + elif filters.get("group_by") == 'Item Group': + return "ORDER BY `tab{0} Item`.{1}".format(doctype, frappe.scrub(filters.get('group_by'))) + elif filters.get("group_by") in ('Customer', 'Customer Group', 'Territory', 'Supplier'): + return "ORDER BY `tab{0}`.{1}".format(doctype, frappe.scrub(filters.get('group_by'))) def get_items(filters, additional_query_columns): conditions = get_conditions(filters) @@ -420,7 +417,7 @@ def get_grand_total(filters, doctype): FROM `tab{0}` WHERE `tab{0}`.docstatus = 1 and posting_date between %s and %s - """.format(doctype), (filters.get('from_date'), filters.get('to_date')))[0][0] + """.format(doctype), (filters.get('from_date'), filters.get('to_date')))[0][0] #nosec def get_deducted_taxes(): return frappe.db.sql_list("select name from `tabPurchase Taxes and Charges` where add_deduct_tax = 'Deduct'") @@ -567,13 +564,22 @@ def add_total_row(data, filters, prev_group_by_value, item, total_row_map, def get_display_value(filters, group_by_field, item): if filters.get('group_by') == 'Item': - return "Item: " + cstr(item.get('item_code')) \ - + "

" + "Item Name: " + cstr(item.get('item_name')) + if item.get('item_code') != item.get('item_name'): + value = item.get('item_code') + "

" + \ + "" + item.get('item_name') + "" + else: + value = cstr(item.get('item_code')) elif filters.get('group_by') in ('Customer', 'Supplier'): - return filters.get('group_by')+": " + cstr(item.get(frappe.scrub(filters.get('group_by')))) \ - + "

" + filters.get('group_by') + " Name: " + item.get(frappe.scrub(filters.get('group_by'))+'_name', '') + party = frappe.scrub(filters.get('group_by')) + if item.get(party) != item.get(party+'_name'): + value = item.get(party) + "

" + \ + "" + item.get(party+'_name') + "" + else: + value = item.get(party) else: - return item.get(group_by_field) + value = item.get(group_by_field) + + return value def get_group_by_and_display_fields(filters): if filters.get('group_by') == 'Item': From b4b410f1d2a2a59828eb8b5aee5d15ce4ebf120f Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 27 Jan 2020 16:06:15 +0530 Subject: [PATCH 057/929] refac: filters and columns of fixed asset register report (#20420) --- .../fixed_asset_register.js | 23 ++++- .../fixed_asset_register.py | 95 ++++++++++++------- 2 files changed, 78 insertions(+), 40 deletions(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index 8c737d066b9..91ce9ce7fe9 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -20,6 +20,16 @@ frappe.query_reports["Fixed Asset Register"] = { default: 'In Location', reqd: 1 }, + { + fieldname:"purchase_date", + label: __("Purchase Date"), + fieldtype: "Date" + }, + { + fieldname:"available_for_use_date", + label: __("Available For Use Date"), + fieldtype: "Date" + }, { fieldname:"finance_book", label: __("Finance Book"), @@ -27,10 +37,15 @@ frappe.query_reports["Fixed Asset Register"] = { options: "Finance Book" }, { - fieldname:"date", - label: __("Date"), - fieldtype: "Date", - default: frappe.datetime.get_today() + fieldname:"asset_category", + label: __("Asset Category"), + fieldtype: "Link", + options: "Asset Category" + }, + { + fieldname:"is_existing_asset", + label: __("Is Existing Asset"), + fieldtype: "Check" }, ] }; diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 14984441d00..fa2fe7b4a3c 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -40,6 +40,42 @@ def get_columns(filters): "fieldname": "status", "width": 90 }, + { + "label": _("Purchase Date"), + "fieldtype": "Date", + "fieldname": "purchase_date", + "width": 90 + }, + { + "label": _("Available For Use Date"), + "fieldtype": "Date", + "fieldname": "available_for_use_date", + "width": 90 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "options": "Currency", + "width": 90 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "options": "Currency", + "width": 90 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "options": "Currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "options": "Currency", + "width": 90 + }, { "label": _("Cost Center"), "fieldtype": "Link", @@ -54,25 +90,6 @@ def get_columns(filters): "options": "Department", "width": 100 }, - { - "label": _("Location"), - "fieldtype": "Link", - "fieldname": "location", - "options": "Location", - "width": 100 - }, - { - "label": _("Purchase Date"), - "fieldtype": "Date", - "fieldname": "purchase_date", - "width": 90 - }, - { - "label": _("Gross Purchase Amount"), - "fieldname": "gross_purchase_amount", - "options": "Currency", - "width": 90 - }, { "label": _("Vendor Name"), "fieldtype": "Data", @@ -80,25 +97,29 @@ def get_columns(filters): "width": 100 }, { - "label": _("Available For Use Date"), - "fieldtype": "Date", - "fieldname": "available_for_use_date", - "width": 90 - }, - { - "label": _("Asset Value"), - "fieldname": "asset_value", - "options": "Currency", - "width": 90 + "label": _("Location"), + "fieldtype": "Link", + "fieldname": "location", + "options": "Location", + "width": 100 }, ] def get_conditions(filters): - conditions = {'docstatus': 1} + conditions = { 'docstatus': 1 } status = filters.status + date = filters.date - if filters.company: + if filters.get('company'): conditions["company"] = filters.company + if filters.get('purchase_date'): + conditions["purchase_date"] = ('<=', filters.get('purchase_date')) + if filters.get('available_for_use_date'): + conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) + if filters.get('is_existing_asset'): + conditions["is_existing_asset"] = filters.get('is_existing_asset') + if filters.get('asset_category'): + conditions["asset_category"] = filters.get('asset_category') # In Store assets are those that are not sold or scrapped operand = 'not in' @@ -114,7 +135,7 @@ def get_data(filters): data = [] conditions = get_conditions(filters) - depreciation_amount_map = get_finance_book_value_map(filters.date, filters.finance_book) + depreciation_amount_map = get_finance_book_value_map(filters) pr_supplier_map = get_purchase_receipt_supplier_map() pi_supplier_map = get_purchase_invoice_supplier_map() @@ -136,6 +157,8 @@ def get_data(filters): "cost_center": asset.cost_center, "vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice), "gross_purchase_amount": asset.gross_purchase_amount, + "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, + "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, "available_for_use_date": asset.available_for_use_date, "location": asset.location, "asset_category": asset.asset_category, @@ -146,9 +169,9 @@ def get_data(filters): return data -def get_finance_book_value_map(date, finance_book=''): - if not date: - date = today() +def get_finance_book_value_map(filters): + date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() + return frappe._dict(frappe.db.sql(''' Select parent, SUM(depreciation_amount) FROM `tabDepreciation Schedule` @@ -157,7 +180,7 @@ def get_finance_book_value_map(date, finance_book=''): AND schedule_date<=%s AND journal_entry IS NOT NULL AND ifnull(finance_book, '')=%s - GROUP BY parent''', (date, cstr(finance_book)))) + GROUP BY parent''', (date, cstr(filters.finance_book or '')))) def get_purchase_receipt_supplier_map(): return frappe._dict(frappe.db.sql(''' Select From 875bac8bb9cf924547cfced624ffb444a9b58165 Mon Sep 17 00:00:00 2001 From: NahuelOperto Date: Mon, 27 Jan 2020 08:58:01 -0300 Subject: [PATCH 058/929] Fix sql injection --- erpnext/setup/doctype/item_group/item_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 22375ae22c3..9f258828660 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -119,7 +119,7 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non or I.name like %(search)s)""" search = "%" + cstr(search) + "%" - query += """order by I.weightage desc, in_stock desc, I.modified desc limit %s, %s""" % (start, limit) + query += """order by I.weightage desc, in_stock desc, I.modified desc limit %s, %s""" % (cint(start), cint(limit)) data = frappe.db.sql(query, {"product_group": product_group,"search": search, "today": nowdate()}, as_dict=1) data = adjust_qty_for_expired_items(data) From 6fbafa1924fe8e7874c0aebeaeee48d2f3b1903b Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Mon, 27 Jan 2020 17:46:09 +0530 Subject: [PATCH 059/929] fix: only fetch driver address if not set --- erpnext/stock/doctype/delivery_trip/delivery_trip.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.json b/erpnext/stock/doctype/delivery_trip/delivery_trip.json index 0a526243bc4..e78bf6b5049 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.json +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.json @@ -163,6 +163,7 @@ }, { "fetch_from": "driver.address", + "fetch_if_empty": 1, "fieldname": "driver_address", "fieldtype": "Link", "label": "Driver Address", @@ -170,7 +171,8 @@ } ], "is_submittable": 1, - "modified": "2019-09-27 15:43:01.975139", + "links": [], + "modified": "2020-01-26 22:37:14.824021", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Trip", From fe56c7ce399839a2cb0f28c5eef02b12cfdf5ce6 Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 28 Jan 2020 12:44:03 +0530 Subject: [PATCH 060/929] fix: Column 'project' in where clause is ambiguous (#20439) * fix: Column 'project' in where clause is ambiguous * fix: Fixed alias in query --- erpnext/accounts/doctype/budget/budget.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py index b76cdf3d1c4..084514cbfa7 100644 --- a/erpnext/accounts/doctype/budget/budget.py +++ b/erpnext/accounts/doctype/budget/budget.py @@ -210,10 +210,10 @@ def get_requested_amount(args, budget): item_code = args.get('item_code') condition = get_other_condition(args, budget, 'Material Request') - data = frappe.db.sql(""" select ifnull((sum(mri.stock_qty - mri.ordered_qty) * rate), 0) as amount - from `tabMaterial Request Item` mri, `tabMaterial Request` mr where mr.name = mri.parent and - mri.item_code = %s and mr.docstatus = 1 and mri.stock_qty > mri.ordered_qty and {0} and - mr.material_request_type = 'Purchase' and mr.status != 'Stopped'""".format(condition), item_code, as_list=1) + data = frappe.db.sql(""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount + from `tabMaterial Request Item` child, `tabMaterial Request` parent where parent.name = child.parent and + child.item_code = %s and parent.docstatus = 1 and child.stock_qty > child.ordered_qty and {0} and + parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(condition), item_code, as_list=1) return data[0][0] if data else 0 @@ -221,10 +221,10 @@ def get_ordered_amount(args, budget): item_code = args.get('item_code') condition = get_other_condition(args, budget, 'Purchase Order') - data = frappe.db.sql(""" select ifnull(sum(poi.amount - poi.billed_amt), 0) as amount - from `tabPurchase Order Item` poi, `tabPurchase Order` po where - po.name = poi.parent and poi.item_code = %s and po.docstatus = 1 and poi.amount > poi.billed_amt - and po.status != 'Closed' and {0}""".format(condition), item_code, as_list=1) + data = frappe.db.sql(""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount + from `tabPurchase Order Item` child, `tabPurchase Order` parent where + parent.name = child.parent and child.item_code = %s and parent.docstatus = 1 and child.amount > child.billed_amt + and parent.status != 'Closed' and {0}""".format(condition), item_code, as_list=1) return data[0][0] if data else 0 @@ -233,16 +233,15 @@ def get_other_condition(args, budget, for_doc): budget_against_field = frappe.scrub(args.get("budget_against_field")) if budget_against_field and args.get(budget_against_field): - condition += " and %s = '%s'" %(budget_against_field, args.get(budget_against_field)) + condition += " and child.%s = '%s'" %(budget_against_field, args.get(budget_against_field)) if args.get('fiscal_year'): date_field = 'schedule_date' if for_doc == 'Material Request' else 'transaction_date' start_date, end_date = frappe.db.get_value('Fiscal Year', args.get('fiscal_year'), ['year_start_date', 'year_end_date']) - alias = 'mr' if for_doc == 'Material Request' else 'po' - condition += """ and %s.%s - between '%s' and '%s' """ %(alias, date_field, start_date, end_date) + condition += """ and parent.%s + between '%s' and '%s' """ %(date_field, start_date, end_date) return condition From f2838c4a134ada97b998745d85cf7bb06035c4eb Mon Sep 17 00:00:00 2001 From: Pranav Nachnekar Date: Tue, 28 Jan 2020 07:22:42 +0000 Subject: [PATCH 061/929] fix: discounting not applied on sales invoice created by subscriptions (#20432) * fix: imporer escaping * fix: discounting not applied on sales invoice created by subscriptions --- erpnext/accounts/doctype/subscription/subscription.json | 6 ++++-- erpnext/accounts/doctype/subscription/subscription.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.json b/erpnext/accounts/doctype/subscription/subscription.json index 29cb62a3972..32b97ba80b5 100644 --- a/erpnext/accounts/doctype/subscription/subscription.json +++ b/erpnext/accounts/doctype/subscription/subscription.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "ACC-SUB-.YYYY.-.#####", "creation": "2017-07-18 17:50:43.967266", "doctype": "DocType", @@ -155,7 +156,7 @@ "fieldname": "apply_additional_discount", "fieldtype": "Select", "label": "Apply Additional Discount On", - "options": "\nGrand Total\nNet total" + "options": "\nGrand Total\nNet Total" }, { "fieldname": "cb_2", @@ -196,7 +197,8 @@ "fieldtype": "Column Break" } ], - "modified": "2019-07-25 18:45:38.579579", + "links": [], + "modified": "2020-01-27 14:37:32.845173", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription", diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 54827503759..98d07c71a3a 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -280,7 +280,7 @@ class Subscription(Document): if self.additional_discount_percentage or self.additional_discount_amount: discount_on = self.apply_additional_discount - invoice.apply_additional_discount = discount_on if discount_on else 'Grand Total' + invoice.apply_discount_on = discount_on if discount_on else 'Grand Total' # Subscription period invoice.from_date = self.current_invoice_start From 637915f295d4879e90ef30cbc6b9ab12820ae5af Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 28 Jan 2020 13:11:49 +0530 Subject: [PATCH 062/929] fix: odometer value was not syncing properly --- erpnext/hr/doctype/vehicle_log/vehicle_log.js | 38 +- .../hr/doctype/vehicle_log/vehicle_log.json | 842 ++++-------------- erpnext/hr/doctype/vehicle_log/vehicle_log.py | 18 +- 3 files changed, 201 insertions(+), 697 deletions(-) diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.js b/erpnext/hr/doctype/vehicle_log/vehicle_log.js index 7694cfed7cc..4c192a0234b 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.js +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.js @@ -2,19 +2,10 @@ // For license information, please see license.txt frappe.ui.form.on("Vehicle Log", { - refresh: function(frm,cdt,cdn) { - var vehicle_log=frappe.model.get_doc(cdt,cdn); - if (vehicle_log.license_plate) { - frappe.call({ - method: "erpnext.hr.doctype.vehicle_log.vehicle_log.get_make_model", - args: { - license_plate: vehicle_log.license_plate - }, - callback: function(r) { - frappe.model.set_value(cdt, cdn, ("model"), r.message[0]); - frappe.model.set_value(cdt, cdn, ("make"), r.message[1]); - } - }) + refresh: function(frm) { + + if(frm.doc.license_plate && frm.doc.__islocal){ + frm.events.set_vehicle_details(frm) } if(frm.doc.docstatus == 1) { @@ -25,6 +16,27 @@ frappe.ui.form.on("Vehicle Log", { } }, + license_plate: function(frm){ + if(frm.doc.license_plate){ + frm.events.set_vehicle_details(frm) + } + }, + + set_vehicle_details: function(frm){ + frappe.call({ + method: "erpnext.hr.doctype.vehicle_log.vehicle_log.get_make_model", + args: { + license_plate: frm.doc.license_plate + }, + callback: function(r) { + frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "model", r.message[0]); + frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "make", r.message[1]); + frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "last_odometer", r.message[2]); + frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "employee", r.message[3]); + } + }); + }, + expense_claim: function(frm){ frappe.call({ method: "erpnext.hr.doctype.vehicle_log.vehicle_log.make_expense_claim", diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.json b/erpnext/hr/doctype/vehicle_log/vehicle_log.json index cde39e7ee44..52effffc064 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.json +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.json @@ -1,706 +1,192 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2016-09-03 14:14:51.788550", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, + "actions": [], + "autoname": "naming_series:", + "creation": "2016-09-03 14:14:51.788550", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "vehicle_section", + "naming_series", + "license_plate", + "employee", + "column_break_4", + "column_break_7", + "model", + "make", + "odometer_reading", + "date", + "odometer", + "column_break_12", + "last_odometer", + "refuelling_details", + "fuel_qty", + "price", + "column_break_15", + "supplier", + "invoice", + "service_details", + "service_detail", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "vehicle_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, - "length": 0, - "no_copy": 0, - "options": "fa fa-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 - }, + "fieldname": "vehicle_section", + "fieldtype": "Section Break", + "options": "fa fa-user" + }, { - "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": "HR-VLOG-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HR-VLOG-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "license_plate", - "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": 0, - "label": "License Plate", - "length": 0, - "no_copy": 0, - "options": "Vehicle", - "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 - }, + "fieldname": "license_plate", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "label": "License Plate", + "options": "Vehicle", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "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": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "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 - }, + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 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": "column_break_7", - "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_7", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "model", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Model", - "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": "model", + "fieldtype": "Read Only", + "label": "Model" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "make", - "fieldtype": "Read Only", - "hidden": 0, - "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", - "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": "make", + "fieldtype": "Read Only", + "label": "Make" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "odometer_reading", - "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": "Odometer Reading", - "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": "odometer_reading", + "fieldtype": "Section Break", + "label": "Odometer Reading" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date", - "length": 0, - "no_copy": 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 - }, + "fieldname": "date", + "fieldtype": "Date", + "label": "Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "odometer", - "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": "Odometer", - "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 - }, + "fieldname": "odometer", + "fieldtype": "Int", + "label": "Current Odometer value ", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "refuelling_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": "Refuelling Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "refuelling_details", + "fieldtype": "Section Break", + "label": "Refuelling Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "fuel_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Fuel Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "fuel_qty", + "fieldtype": "Float", + "label": "Fuel Qty" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "price", - "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": "Fuel Price", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "price", + "fieldtype": "Currency", + "label": "Fuel Price" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_15", - "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_15", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "supplier", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Supplier", - "length": 0, - "no_copy": 0, - "options": "Supplier", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "options": "Supplier" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "invoice", - "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": "Invoice Ref", - "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": "invoice", + "fieldtype": "Data", + "label": "Invoice Ref" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "service_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": "Service Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "service_details", + "fieldtype": "Section Break", + "label": "Service Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "service_detail", - "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": "Service Detail", - "length": 0, - "no_copy": 0, - "options": "Vehicle Service", - "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": "service_detail", + "fieldtype": "Table", + "label": "Service Detail", + "options": "Vehicle Service" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Vehicle Log", - "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", + "label": "Amended From", + "no_copy": 1, + "options": "Vehicle Log", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "last_odometer", + "fieldtype": "Int", + "label": "last Odometer Value ", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:51.131186", - "modified_by": "Administrator", - "module": "HR", - "name": "Vehicle Log", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-01-28 12:43:34.419647", + "modified_by": "Administrator", + "module": "HR", + "name": "Vehicle Log", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Fleet Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Fleet Manager", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.py b/erpnext/hr/doctype/vehicle_log/vehicle_log.py index df633611be8..0dbf1ceb961 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.py @@ -11,22 +11,28 @@ from frappe.model.document import Document class VehicleLog(Document): def validate(self): - last_odometer=frappe.db.get_value("Vehicle", self.license_plate, "last_odometer") - if flt(self.odometer) < flt(last_odometer): - frappe.throw(_("Current Odometer reading entered should be greater than initial Vehicle Odometer {0}").format(last_odometer)) + if flt(self.odometer) < flt(self.last_odometer): + frappe.throw(_("Current Odometer reading entered should be greater than initial Vehicle Odometer {0}").format(self.last_odometer)) for service_detail in self.service_detail: if (service_detail.service_item or service_detail.type or service_detail.frequency or service_detail.expense_amount): if not (service_detail.service_item and service_detail.type and service_detail.frequency and service_detail.expense_amount): frappe.throw(_("Service Item,Type,frequency and expense amount are required")) def on_submit(self): - frappe.db.sql("update `tabVehicle` set last_odometer=%s where license_plate=%s", - (self.odometer, self.license_plate)) + print("I am here") + frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", self.odometer) + + def on_cancel(self): + print("sel"*10, self.last_odometer, self.odometer) + distance_travelled = self.odometer - self.last_odometer + if(distance_travelled > 0): + updated_odometer_value = int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled + frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", updated_odometer_value) @frappe.whitelist() def get_make_model(license_plate): vehicle=frappe.get_doc("Vehicle",license_plate) - return (vehicle.make,vehicle.model) + return (vehicle.make,vehicle.model,vehicle.last_odometer,vehicle.employee) @frappe.whitelist() def make_expense_claim(docname): From df94f0565ea1f83bb9f1298de13b0775708cde35 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 Jan 2020 14:08:48 +0530 Subject: [PATCH 063/929] fix: cannot complete task if dependent task are not completed / cancelled (#20434) --- erpnext/projects/doctype/task/task.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 45f26814a65..f2ba80f096f 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -56,8 +56,8 @@ class Task(NestedSet): def validate_status(self): if self.status!=self.get_db_value("status") and self.status == "Completed": for d in self.depends_on: - if frappe.db.get_value("Task", d.task, "status") != "Completed": - frappe.throw(_("Cannot close task {0} as its dependant task {1} is not closed.").format(frappe.bold(self.name), frappe.bold(d.task))) + if frappe.db.get_value("Task", d.task, "status") not in ("Completed", "Cancelled"): + frappe.throw(_("Cannot complete task {0} as its dependant tasks {1} are not completed / cancelled.").format(frappe.bold(self.name), frappe.bold(d.task))) close_all_assignments(self.doctype, self.name) From 8e5b812a97d86eb73e0f7c396ce3fcd232b4078a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 28 Jan 2020 14:34:54 +0530 Subject: [PATCH 064/929] fix: incorrect work order qty for sub assembly items (#20448) --- .../manufacturing/doctype/production_plan/production_plan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 25c385fb1e5..10a8d8fc9bd 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -729,6 +729,6 @@ def get_sub_assembly_items(bom_no, bom_data): }) bom_item = bom_data.get(key) - bom_item["stock_qty"] += d.stock_qty + bom_item["stock_qty"] += d.stock_qty / d.parent_bom_qty get_sub_assembly_items(bom_item.get("bom_no"), bom_data) From d4d641a07ea886d26c43f107cfc446cc965df7bd Mon Sep 17 00:00:00 2001 From: 0Pranav Date: Wed, 29 Jan 2020 09:32:40 +0530 Subject: [PATCH 065/929] fix: missing plus button in request for quotaion for supplier quotation --- .../doctype/request_for_quotation/request_for_quotation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 2f0cfa64fc8..455bd68ecff 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -9,7 +9,7 @@ cur_frm.add_fetch('contact', 'email_id', 'email_id') frappe.ui.form.on("Request for Quotation",{ setup: function(frm) { frm.custom_make_buttons = { - 'Supplier Quotation': 'Supplier Quotation' + 'Supplier Quotation': 'Create' } frm.fields_dict["suppliers"].grid.get_field("contact").get_query = function(doc, cdt, cdn) { From 7524ecbc59d33efc454c4f0904057f81379969ae Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 30 Jan 2020 00:08:25 +0100 Subject: [PATCH 066/929] fix #20469 --- erpnext/regional/report/datev/datev.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index 589165173ac..a58e13a0947 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -160,7 +160,7 @@ def get_gl_entries(filters, as_dict): where gl.company = %(company)s and DATE(gl.posting_date) >= %(from_date)s and DATE(gl.posting_date) <= %(to_date)s - order by 'Belegdatum', gl.voucher_no""", filters, as_dict=as_dict, as_utf8=1) + order by 'Belegdatum', gl.voucher_no""", filters, as_dict=as_dict) return gl_entries From e552e958de3d3780ee2474b7a3bc2af29b973e8e Mon Sep 17 00:00:00 2001 From: Parth Kharwar Date: Thu, 30 Jan 2020 12:12:42 +0530 Subject: [PATCH 067/929] fix: rename breadcrumb for Brand DocType from Selling to Stock --- 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 095e744926b..0f88f133501 100644 --- a/erpnext/public/js/conf.js +++ b/erpnext/public/js/conf.js @@ -50,7 +50,7 @@ $.extend(frappe.breadcrumbs.preferred, { "Territory": "Selling", "Sales Person": "Selling", "Sales Partner": "Selling", - "Brand": "Selling" + "Brand": "Stock" }); $.extend(frappe.breadcrumbs.module_map, { From ba4d9469ffd974001e4caab04beeda21f135a0b9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 30 Jan 2020 15:33:51 +0530 Subject: [PATCH 068/929] fix: Do not show any finance book record if no finance book filter is applied --- erpnext/accounts/report/cash_flow/cash_flow.py | 2 ++ erpnext/accounts/report/financial_statements.py | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index 0b124774479..702c81ccb76 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -136,6 +136,8 @@ def get_account_type_based_gl_data(company, start_date, end_date, account_type, cond = """ AND (finance_book in (%s, %s, '') OR finance_book IS NULL) """ %(frappe.db.escape(filters.finance_book), frappe.db.escape(company_fb)) + else: + cond = "AND (finance_book = '' OR finance_book IS NULL)" gl_sum = frappe.db.sql_list(""" select sum(credit) - sum(debit) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 32d9075f443..a1ecaae2a11 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -13,7 +13,7 @@ import frappe, erpnext from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency from erpnext.accounts.utils import get_fiscal_year from frappe import _ -from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate) +from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate, cstr) from six import itervalues from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions @@ -356,7 +356,7 @@ def set_gl_entries_by_account( "company": company, "from_date": from_date, "to_date": to_date, - "finance_book": filters.get("finance_book") + "finance_book": cstr(filters.get("finance_book")) } if filters.get("include_default_book_entries"): @@ -411,6 +411,8 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") else: additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") + else: + additional_conditions.append("(finance_book = '' OR finance_book IS NULL)") if accounting_dimensions: for dimension in accounting_dimensions: From a175e06149b130dce7433a95c532d98e4d59d3f6 Mon Sep 17 00:00:00 2001 From: racitup Date: Mon, 20 Jan 2020 18:56:35 +0000 Subject: [PATCH 069/929] fix: pymysql.err.InternalError about t2.bank_account_no due to removal of field from Journal Entry Account table: #20343 --- .../doctype/bank_reconciliation/bank_reconciliation.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 90cdf834c59..3613a1ef696 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -21,10 +21,6 @@ class BankReconciliation(Document): if not self.include_reconciled_entries: condition = " and (clearance_date is null or clearance_date='0000-00-00')" - account_cond = "" - if self.bank_account_no: - account_cond = " and t2.bank_account_no = {0}".format(frappe.db.escape(self.bank_account_no)) - journal_entries = frappe.db.sql(""" select "Journal Entry" as payment_document, t1.name as payment_entry, @@ -36,10 +32,10 @@ class BankReconciliation(Document): where t2.parent = t1.name and t2.account = %s and t1.docstatus=1 and t1.posting_date >= %s and t1.posting_date <= %s - and ifnull(t1.is_opening, 'No') = 'No' {0} {1} + and ifnull(t1.is_opening, 'No') = 'No' {0} group by t2.account, t1.name order by t1.posting_date ASC, t1.name DESC - """.format(condition, account_cond), (self.bank_account, self.from_date, self.to_date), as_dict=1) + """.format(condition), (self.bank_account, self.from_date, self.to_date), as_dict=1) if self.bank_account_no: condition = " and bank_account = %(bank_account_no)s" From feaa82a8eab5b5c415cdbff718b6eb64b1aaf9d6 Mon Sep 17 00:00:00 2001 From: racitup Date: Mon, 20 Jan 2020 19:41:08 +0000 Subject: [PATCH 070/929] fix: KeyError about bank_account_no due to non-existent field: #20343 --- .../doctype/bank_reconciliation/bank_reconciliation.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 3613a1ef696..d5ba49abd0f 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -68,11 +68,10 @@ class BankReconciliation(Document): from `tabSales Invoice Payment` sip, `tabSales Invoice` si, `tabAccount` account where sip.account=%(account)s and si.docstatus=1 and sip.parent = si.name - and account.name = sip.account and si.posting_date >= %(from)s and si.posting_date <= %(to)s {0} + and account.name = sip.account and si.posting_date >= %(from)s and si.posting_date <= %(to)s order by si.posting_date ASC, si.name DESC - """.format(condition), - {"account":self.bank_account, "from":self.from_date, "to":self.to_date}, as_dict=1) + """, {"account":self.bank_account, "from":self.from_date, "to":self.to_date}, as_dict=1) entries = sorted(list(payment_entries)+list(journal_entries+list(pos_entries)), key=lambda k: k['posting_date'] or getdate(nowdate())) From a05450821182bce276f1242761b5b2a670b03663 Mon Sep 17 00:00:00 2001 From: racitup Date: Tue, 21 Jan 2020 00:47:10 +0000 Subject: [PATCH 071/929] fix: Plaid automatic_synchronization TypeError on filter & add info log message #20343 --- .../doctype/plaid_settings/plaid_settings.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index a04d6c5be73..9e22dc07bb6 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -133,8 +133,11 @@ def sync_transactions(bank, bank_account): try: transactions = get_transactions(bank=bank, bank_account=bank_account, start_date=start_date, end_date=end_date) + result = [] if transactions: + frappe.logger().info("Plaid is adding {} Bank Transactions from '{}' between {} and {}".format( + len(transactions), bank_account, start_date, end_date)) for transaction in transactions: result.append(new_bank_transaction(transaction)) @@ -201,7 +204,7 @@ def automatic_synchronization(): settings = frappe.get_doc("Plaid Settings", "Plaid Settings") if settings.enabled == 1 and settings.automatic_sync == 1: - plaid_accounts = frappe.get_all("Bank Account", filter={"integration_id": ["!=", ""]}, fields=["name", "bank"]) + plaid_accounts = frappe.get_all("Bank Account", filters={"integration_id": ["!=", ""]}, fields=["name", "bank"]) for plaid_account in plaid_accounts: frappe.enqueue("erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions", bank=plaid_account.bank, bank_account=plaid_account.name) From bee017e17c78aed52dee5ebf4f92fa4184dfa08e Mon Sep 17 00:00:00 2001 From: racitup Date: Tue, 21 Jan 2020 15:07:47 +0000 Subject: [PATCH 072/929] fix: Plaid transaction import order, transaction_id duplicate check, added transaction category tags --- .../doctype/plaid_settings/plaid_settings.py | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index 9e22dc07bb6..970793f7468 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -10,6 +10,7 @@ from frappe.model.document import Document from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_connector import PlaidConnector from frappe.utils import getdate, formatdate, today, add_months +from frappe.desk.doctype.tag.tag import add_tag class PlaidSettings(Document): pass @@ -135,11 +136,11 @@ def sync_transactions(bank, bank_account): transactions = get_transactions(bank=bank, bank_account=bank_account, start_date=start_date, end_date=end_date) result = [] - if transactions: - frappe.logger().info("Plaid is adding {} Bank Transactions from '{}' between {} and {}".format( - len(transactions), bank_account, start_date, end_date)) - for transaction in transactions: - result.append(new_bank_transaction(transaction)) + for transaction in reversed(transactions): + result += new_bank_transaction(transaction) + + frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( + len(result), bank_account, start_date, end_date)) frappe.db.set_value("Bank Account", bank_account, "last_integration_date", getdate(end_date)) @@ -178,6 +179,13 @@ def new_bank_transaction(transaction): status = "Pending" if transaction["pending"] == "True" else "Settled" + try: + tags = [] + tags += transaction["category"] + tags += ["Plaid Cat. {}".format(transaction["category_id"])] + except KeyError: + pass + if not frappe.db.exists("Bank Transaction", dict(transaction_id=transaction["transaction_id"])): try: new_transaction = frappe.get_doc({ @@ -188,11 +196,16 @@ def new_bank_transaction(transaction): "debit": debit, "credit": credit, "currency": transaction["iso_currency_code"], + "transaction_id": transaction["transaction_id"], + "reference_number": transaction["payment_meta"]["reference_number"], "description": transaction["name"] }) new_transaction.insert() new_transaction.submit() + for tag in tags: + add_tag(tag, "Bank Transaction", new_transaction.name) + result.append(new_transaction.name) except Exception: From a1e3202054235d7b7ab30ec3b3b3cb78eabf86b0 Mon Sep 17 00:00:00 2001 From: racitup Date: Wed, 22 Jan 2020 04:32:15 +0000 Subject: [PATCH 073/929] fix: Bank Reconciliation Bank Account and Bank Account No field names --- .../bank_reconciliation.js | 6 +++--- .../bank_reconciliation.json | 18 +++++++--------- .../bank_reconciliation.py | 21 ++++++++----------- .../rename_bank_reconciliation_fields.py | 16 ++++++++++++++ 4 files changed, 36 insertions(+), 25 deletions(-) create mode 100644 erpnext/patches/v12_0/rename_bank_reconciliation_fields.py diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js index 7c94455b9ba..19fadbf6ded 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js @@ -3,16 +3,16 @@ frappe.ui.form.on("Bank Reconciliation", { setup: function(frm) { - frm.add_fetch("bank_account", "account_currency", "account_currency"); + frm.add_fetch("account", "account_currency", "account_currency"); }, onload: function(frm) { let default_bank_account = frappe.defaults.get_user_default("Company")? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: ""; - frm.set_value("bank_account", default_bank_account); + frm.set_value("account", default_bank_account); - frm.set_query("bank_account", function() { + frm.set_query("account", function() { return { "filters": { "account_type": ["in",["Bank","Cash"]], diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json index e2f967daccb..b85ef3e9c4d 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json @@ -19,10 +19,9 @@ "bold": 0, "collapsible": 0, "columns": 0, - "description": "Select account head of the bank where cheque was deposited.", - "fetch_from": "bank_account_no.account", + "fetch_from": "bank_account.account", "fetch_if_empty": 1, - "fieldname": "bank_account", + "fieldname": "account", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -31,7 +30,7 @@ "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, - "label": "Bank Account", + "label": "Account", "length": 0, "no_copy": 0, "options": "Account", @@ -164,7 +163,6 @@ "length": 0, "no_copy": 0, "permlevel": 0, - "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, @@ -183,8 +181,9 @@ "bold": 0, "collapsible": 0, "columns": 0, + "description": "Select the Bank Account to reconcile.", "fetch_if_empty": 0, - "fieldname": "bank_account_no", + "fieldname": "bank_account", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -193,12 +192,11 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Bank Account No", + "label": "Bank Account", "length": 0, "no_copy": 0, "options": "Bank Account", "permlevel": 0, - "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, @@ -450,7 +448,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2019-04-09 18:41:06.110453", + "modified": "2020-01-22 00:00:00.000000", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Reconciliation", @@ -483,4 +481,4 @@ "track_changes": 0, "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index d5ba49abd0f..804bcd8bdf0 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -30,15 +30,12 @@ class BankReconciliation(Document): from `tabJournal Entry` t1, `tabJournal Entry Account` t2 where - t2.parent = t1.name and t2.account = %s and t1.docstatus=1 - and t1.posting_date >= %s and t1.posting_date <= %s - and ifnull(t1.is_opening, 'No') = 'No' {0} + t2.parent = t1.name and t2.account = %(account)s and t1.docstatus=1 + and t1.posting_date >= %(from)s and t1.posting_date <= %(to)s + and ifnull(t1.is_opening, 'No') = 'No' %(condition)s group by t2.account, t1.name order by t1.posting_date ASC, t1.name DESC - """.format(condition), (self.bank_account, self.from_date, self.to_date), as_dict=1) - - if self.bank_account_no: - condition = " and bank_account = %(bank_account_no)s" + """, {"condition":condition, "account":self.account, "from":self.from_date, "to":self.to_date}, as_dict=1) payment_entries = frappe.db.sql(""" select @@ -51,12 +48,12 @@ class BankReconciliation(Document): from `tabPayment Entry` where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1 - and posting_date >= %(from)s and posting_date <= %(to)s {0} + and posting_date >= %(from)s and posting_date <= %(to)s + and bank_account = %(bank_account)s order by posting_date ASC, name DESC - """.format(condition), - {"account":self.bank_account, "from":self.from_date, - "to":self.to_date, "bank_account_no": self.bank_account_no}, as_dict=1) + """, {"account":self.account, "from":self.from_date, + "to":self.to_date, "bank_account": self.bank_account}, as_dict=1) pos_entries = [] if self.include_pos_transactions: @@ -71,7 +68,7 @@ class BankReconciliation(Document): and account.name = sip.account and si.posting_date >= %(from)s and si.posting_date <= %(to)s order by si.posting_date ASC, si.name DESC - """, {"account":self.bank_account, "from":self.from_date, "to":self.to_date}, as_dict=1) + """, {"account":self.account, "from":self.from_date, "to":self.to_date}, as_dict=1) entries = sorted(list(payment_entries)+list(journal_entries+list(pos_entries)), key=lambda k: k['posting_date'] or getdate(nowdate())) diff --git a/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py b/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py new file mode 100644 index 00000000000..8918b9df7a0 --- /dev/null +++ b/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py @@ -0,0 +1,16 @@ +import frappe + +def _rename_single_field(**kwargs): + count = frappe.db.sql("SELECT COUNT(*) FROM tabSingles WHERE doctype='{doctype}' AND field='{new_name}';".format(**kwargs))[0][0] + if count == 0: + frappe.db.sql("UPDATE tabSingles SET field='{new_name}' WHERE doctype='{doctype}' AND field='{old_name}';".format(**kwargs)) + +def execute(): + BR = "Bank Reconciliation" + AC = "account" + BA = "bank_account" + BAN = "bank_account_no" + + _rename_single_field(doctype = BR, old_name = BA , new_name = AC) + _rename_single_field(doctype = BR, old_name = BAN, new_name = BA) + frappe.reload_doc("Accounts", "doctype", BR) From eef73a0d920598f5722ae324aed18a274b23be47 Mon Sep 17 00:00:00 2001 From: racitup Date: Fri, 24 Jan 2020 23:40:16 +0000 Subject: [PATCH 074/929] manually added patch for bank reconciliation fields since develop branch was in a different place #20380 --- erpnext/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 17c9a20766d..bbb875a3191 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -648,3 +648,4 @@ erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger erpnext.patches.v12_0.update_price_or_product_discount erpnext.patches.v12_0.add_export_type_field_in_party_master +erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22 From 814001a90fcf84e910136cfead34973cdef434e5 Mon Sep 17 00:00:00 2001 From: racitup Date: Wed, 22 Jan 2020 04:41:41 +0000 Subject: [PATCH 075/929] fix: attempt at pymysql InternalError 1054 about clearance_date in field list when removing payments from bank transactions --- .../bank_transaction_payments.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.json b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.json index a75e866997d..ab3f60d32c0 100644 --- a/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.json +++ b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.json @@ -110,6 +110,15 @@ "set_only_once": 0, "translatable": 0, "unique": 0 + }, + { + "depends_on": "eval:doc.docstatus==1", + "fieldname": "clearance_date", + "fieldtype": "Date", + "label": "Clearance Date", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "has_web_view": 0, @@ -122,7 +131,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-12-06 10:57:02.635141", + "modified": "2020-01-22 00:00:00.000000", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Transaction Payments", @@ -138,4 +147,4 @@ "track_changes": 1, "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} From 08661250d231f07a5068db3903a6bb16134c5eed Mon Sep 17 00:00:00 2001 From: racitup Date: Thu, 30 Jan 2020 14:48:42 +0000 Subject: [PATCH 076/929] Ignore codacy SQL injection warning (internal code only) and add copyright notice --- erpnext/patches/v12_0/rename_bank_reconciliation_fields.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py b/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py index 8918b9df7a0..01b1dde6073 100644 --- a/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py +++ b/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py @@ -1,9 +1,12 @@ +# Copyright (c) 2020, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + import frappe def _rename_single_field(**kwargs): - count = frappe.db.sql("SELECT COUNT(*) FROM tabSingles WHERE doctype='{doctype}' AND field='{new_name}';".format(**kwargs))[0][0] + count = frappe.db.sql("SELECT COUNT(*) FROM tabSingles WHERE doctype='{doctype}' AND field='{new_name}';".format(**kwargs))[0][0] #nosec if count == 0: - frappe.db.sql("UPDATE tabSingles SET field='{new_name}' WHERE doctype='{doctype}' AND field='{old_name}';".format(**kwargs)) + frappe.db.sql("UPDATE tabSingles SET field='{new_name}' WHERE doctype='{doctype}' AND field='{old_name}';".format(**kwargs)) #nosec def execute(): BR = "Bank Reconciliation" From 9826291c524f0b09c341e0b6501c918205897435 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 31 Jan 2020 09:08:29 +0530 Subject: [PATCH 077/929] fix: Filtering fixes in financial statement --- erpnext/accounts/report/cash_flow/cash_flow.py | 9 ++++----- .../consolidated_financial_statement.py | 9 ++++----- erpnext/accounts/report/financial_statements.py | 11 ++++------- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index 702c81ccb76..3c2aa654d98 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import cint +from frappe.utils import cint, cstr from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data) from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss from erpnext.accounts.utils import get_fiscal_year @@ -130,14 +130,13 @@ def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters = frappe._dict(filters) if filters.finance_book: - cond = " AND (finance_book in (%s, '') OR finance_book IS NULL)" %(frappe.db.escape(filters.finance_book)) if filters.include_default_book_entries: company_fb = frappe.db.get_value("Company", company, 'default_finance_book') - cond = """ AND (finance_book in (%s, %s, '') OR finance_book IS NULL) """ %(frappe.db.escape(filters.finance_book), frappe.db.escape(company_fb)) - else: - cond = "AND (finance_book = '' OR finance_book IS NULL)" + else: + cond = " AND (finance_book in (%s, '') OR finance_book IS NULL)" %(frappe.db.escape(cstr(filters.finance_book))) + gl_sum = frappe.db.sql_list(""" select sum(credit) - sum(debit) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index e9eb819cc64..4a79b6a340e 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -387,11 +387,10 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): if from_date: additional_conditions.append("gl.posting_date >= %(from_date)s") - if filters.get("finance_book"): - if filters.get("include_default_book_entries"): - additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") - else: - additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") + if filters.get("include_default_book_entries"): + additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") + else: + additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else "" diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index a1ecaae2a11..57e8e0ea8fc 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -406,14 +406,11 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): filters.cost_center = get_cost_centers_with_children(filters.cost_center) additional_conditions.append("cost_center in %(cost_center)s") - if filters.get("finance_book"): - if filters.get("include_default_book_entries"): - additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") - else: - additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") + if filters.get("include_default_book_entries"): + additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") else: - additional_conditions.append("(finance_book = '' OR finance_book IS NULL)") - + additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") + if accounting_dimensions: for dimension in accounting_dimensions: if filters.get(dimension): From bb7cf81d52facee21971aa353cd3193020535fe6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 31 Jan 2020 11:16:30 +0530 Subject: [PATCH 078/929] fix: Cash flow filter fix --- erpnext/accounts/report/cash_flow/cash_flow.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index 3c2aa654d98..e349a6aaaf0 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -129,14 +129,13 @@ def get_account_type_based_gl_data(company, start_date, end_date, account_type, cond = "" filters = frappe._dict(filters) - if filters.finance_book: - if filters.include_default_book_entries: - company_fb = frappe.db.get_value("Company", company, 'default_finance_book') - cond = """ AND (finance_book in (%s, %s, '') OR finance_book IS NULL) - """ %(frappe.db.escape(filters.finance_book), frappe.db.escape(company_fb)) - else: - cond = " AND (finance_book in (%s, '') OR finance_book IS NULL)" %(frappe.db.escape(cstr(filters.finance_book))) - + if filters.include_default_book_entries: + company_fb = frappe.db.get_value("Company", company, 'default_finance_book') + cond = """ AND (finance_book in (%s, %s, '') OR finance_book IS NULL) + """ %(frappe.db.escape(filters.finance_book), frappe.db.escape(company_fb)) + else: + cond = " AND (finance_book in (%s, '') OR finance_book IS NULL)" %(frappe.db.escape(cstr(filters.finance_book))) + gl_sum = frappe.db.sql_list(""" select sum(credit) - sum(debit) From 43886ca01676a8856f430a914e33f27ea37c89fc Mon Sep 17 00:00:00 2001 From: "Parth J. Kharwar" Date: Fri, 31 Jan 2020 14:37:56 +0530 Subject: [PATCH 079/929] fix: employee checkin doctype time permissions (#20473) --- .../employee_checkin/employee_checkin.js | 8 ++- .../employee_checkin/employee_checkin.json | 60 ++++++++++++++++++- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.js b/erpnext/hr/doctype/employee_checkin/employee_checkin.js index f11cc9b2522..c2403ca2bd9 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.js +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.js @@ -2,7 +2,9 @@ // For license information, please see license.txt frappe.ui.form.on('Employee Checkin', { - // refresh: function(frm) { - - // } + setup: (frm) => { + if(!frm.doc.time) { + frm.set_value("time", frappe.datetime.now_datetime()); + } + } }); diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.json b/erpnext/hr/doctype/employee_checkin/employee_checkin.json index 08fa4afa5cb..75f699751b8 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.json +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "EMP-CKIN-.MM.-.YYYY.-.######", "creation": "2019-06-10 11:56:34.536413", @@ -23,7 +24,6 @@ { "fieldname": "employee", "fieldtype": "Link", - "in_list_view": 1, "label": "Employee", "options": "Employee", "reqd": 1 @@ -32,14 +32,17 @@ "fetch_from": "employee.employee_name", "fieldname": "employee_name", "fieldtype": "Data", + "in_list_view": 1, "label": "Employee Name", "read_only": 1 }, { "fieldname": "log_type", "fieldtype": "Select", + "in_list_view": 1, "label": "Log Type", - "options": "\nIN\nOUT" + "options": "\nIN\nOUT", + "reqd": 1 }, { "fieldname": "shift", @@ -58,6 +61,7 @@ "fieldtype": "Datetime", "in_list_view": 1, "label": "Time", + "permlevel": 1, "reqd": 1 }, { @@ -103,7 +107,8 @@ "label": "Shift Actual End" } ], - "modified": "2019-07-23 23:47:33.975263", + "links": [], + "modified": "2020-01-23 04:57:42.551355", "modified_by": "Administrator", "module": "HR", "name": "Employee Checkin", @@ -147,9 +152,58 @@ "role": "HR User", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "read": 1, + "role": "Employee", + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "role": "Employee" } ], "sort_field": "modified", "sort_order": "ASC", + "title_field": "employee_name", "track_changes": 1 } \ No newline at end of file From 831a7ddd6f43b58f3625edf85d8f74f729736ccb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 1 Feb 2020 23:14:11 +0530 Subject: [PATCH 080/929] fix: Minor fixes --- .../item_wise_sales_register.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index cf48ad4701d..dc900f09e36 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -106,7 +106,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum group_by_field, subtotal_display_field = get_group_by_and_display_fields(filters) data, prev_group_by_value = add_total_row(data, filters, prev_group_by_value, d, total_row_map, group_by_field, subtotal_display_field, grand_total) - update_total_row(row, total_row_map, d.get(group_by_field, ''), tax_columns) + add_sub_total_row(row, total_row_map, d.get(group_by_field, ''), tax_columns) data.append(row) @@ -565,10 +565,10 @@ def add_total_row(data, filters, prev_group_by_value, item, total_row_map, def get_display_value(filters, group_by_field, item): if filters.get('group_by') == 'Item': if item.get('item_code') != item.get('item_name'): - value = item.get('item_code') + "

" + \ - "" + item.get('item_name') + "" + value = cstr(item.get('item_code')) + "

" + \ + "" + cstr(item.get('item_name')) + "" else: - value = cstr(item.get('item_code')) + value = item.get('item_code', '') elif filters.get('group_by') in ('Customer', 'Supplier'): party = frappe.scrub(filters.get('group_by')) if item.get(party) != item.get(party+'_name'): @@ -594,7 +594,7 @@ def get_group_by_and_display_fields(filters): return group_by_field, subtotal_display_field -def update_total_row(item, total_row_map, group_by_value, tax_columns): +def add_sub_total_row(item, total_row_map, group_by_value, tax_columns): total_row = total_row_map.get(group_by_value) total_row['stock_qty'] += item['stock_qty'] total_row['amount'] += item['amount'] From a4219f1cfe8bba35134f6bbc0df80d75a5aaf7a0 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 3 Feb 2020 15:40:53 +0530 Subject: [PATCH 081/929] fix: gst permission for gst settings & hsn code (#20501) * fix: gst permission for gst settings & hsn code * Fix: Typo Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/regional/india/setup.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 14fdba013c7..cabfde40ef4 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -79,9 +79,10 @@ def add_custom_roles_for_reports(): def add_permissions(): for doctype in ('GST HSN Code', 'GST Settings'): add_permission(doctype, 'All', 0) - add_permission(doctype, 'Accounts Manager', 0) - update_permission_property(doctype, 'Accounts Manager', 0, 'write', 1) - update_permission_property(doctype, 'Accounts Manager', 0, 'create', 1) + for role in ('Accounts Manager', 'System Manager', 'Item Manager', 'Stock Manager'): + add_permission(doctype, role, 0) + update_permission_property(doctype, role, 0, 'write', 1) + update_permission_property(doctype, role, 0, 'create', 1) def add_print_formats(): frappe.reload_doc("regional", "print_format", "gst_tax_invoice") @@ -718,4 +719,4 @@ def get_tds_details(accounts, fiscal_year): doctype="Tax Withholding Category", accounts=accounts, rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20, "single_threshold": 2500, "cumulative_threshold": 0}]) - ] \ No newline at end of file + ] From f3c94315c36da6b071f64fb5297924d39fbd5695 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 3 Feb 2020 15:53:22 +0530 Subject: [PATCH 082/929] feat: add tax category in pos profile (#20414) * feat: add tax category in pos profile * fix: review fixes --- erpnext/accounts/doctype/pos_profile/pos_profile.json | 10 +++++++++- .../accounts/doctype/sales_invoice/sales_invoice.py | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index aa9f85a05ab..fba1bed9dd1 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -3,6 +3,7 @@ "autoname": "Prompt", "creation": "2013-05-24 12:15:51", "doctype": "DocType", + "engine": "InnoDB", "field_order": [ "disabled", "section_break_2", @@ -50,6 +51,7 @@ "income_account", "expense_account", "taxes_and_charges", + "tax_category", "apply_discount_on", "accounting_dimensions_section", "cost_center", @@ -381,11 +383,17 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "tax_category", + "fieldtype": "Link", + "label": "Tax Category", + "options": "Tax Category" } ], "icon": "icon-cog", "idx": 1, - "modified": "2019-05-25 22:56:30.352693", + "modified": "2020-01-24 15:52:03.797701", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a421cc4d6dc..0dc889c0b7a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -412,6 +412,9 @@ class SalesInvoice(SellingController): if pos: self.allow_print_before_pay = pos.allow_print_before_pay + + if not for_validate: + self.tax_category = pos.get("tax_category") if not for_validate and not self.customer: self.customer = pos.customer From 85c6480b412cc3d76c180fbc80b65afe6061ba18 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Mon, 3 Feb 2020 16:16:42 +0530 Subject: [PATCH 083/929] fix(Report): Quality Review report sql fix (#20425) * fix: report fix * fix: add QM permission * fix: add QM permission --- .../quality_review/quality_review.json | 14 ++++++- .../report/review/review.json | 38 ++++++++++--------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.json b/erpnext/quality_management/doctype/quality_review/quality_review.json index bd5e9351f46..a345dac026d 100644 --- a/erpnext/quality_management/doctype/quality_review/quality_review.json +++ b/erpnext/quality_management/doctype/quality_review/quality_review.json @@ -73,7 +73,7 @@ "reqd": 1 } ], - "modified": "2019-05-26 23:12:47.302189", + "modified": "2020-02-01 10:57:27.119312", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Review", @@ -102,6 +102,18 @@ "role": "All", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, + "write": 1 } ], "sort_field": "modified", diff --git a/erpnext/quality_management/report/review/review.json b/erpnext/quality_management/report/review/review.json index 7fce2d4c57c..7fff0fdca6c 100644 --- a/erpnext/quality_management/report/review/review.json +++ b/erpnext/quality_management/report/review/review.json @@ -1,24 +1,28 @@ { - "add_total_row": 0, - "creation": "2018-10-16 12:28:43.651915", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2018-10-16 15:23:25.667237", - "modified_by": "Administrator", - "module": "Quality Management", - "name": "Review", - "owner": "Administrator", - "prepared_report": 0, - "query": "SELECT\n `tabQuality Action`.name as \"Name:Data:200\",\n `tabQuality Action`.action as \"Action:Select/[corrective,Preventive]:200\",\n `tabQuality Action`.review as \"Review:Link/Quality Review:200\",\n `tabQuality Action`.date as \"Date:Date:120\",\n `tabQuality Action`.status as \"Status:Select/Planned:150\"\nFROM\n `tabQuality Action`\nWHERE\n `tabQuality Action`.type='Quality Review'\n \n ", - "ref_doctype": "Quality Action", - "report_name": "Review", - "report_type": "Query Report", + "add_total_row": 0, + "creation": "2018-10-16 12:28:43.651915", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-02-01 11:04:47.299453", + "modified_by": "Administrator", + "module": "Quality Management", + "name": "Review", + "owner": "Administrator", + "prepared_report": 0, + "query": "SELECT\n `tabQuality Action`.name as \"Name:Data:200\",\n `tabQuality Action`.corrective_preventive as \"Action:Select/[Corrective,Preventive]:200\",\n `tabQuality Action`.document_type as \"Document Type:Select/[Quality Review, Quality Feedback]:200\",\n `tabQuality Action`.date as \"Date:Date:120\",\n `tabQuality Action`.status as \"Status:Select/Planned:150\"\nFROM\n `tabQuality Action`\nWHERE\n `tabQuality Action`.document_type='Quality Review'\n \n ", + "ref_doctype": "Quality Action", + "report_name": "Review", + "report_type": "Query Report", "roles": [ { "role": "System Manager" + }, + { + "role": "Quality Manager" } ] } \ No newline at end of file From 3ef80f2d2eee76dddf85924dd6ca93d912d5cb5c Mon Sep 17 00:00:00 2001 From: Pranav Nachnekar Date: Mon, 3 Feb 2020 11:35:30 +0000 Subject: [PATCH 084/929] fix: disallow quick entry for doctypes with tree view (#20453) * fix: imporer escaping * fix: disallow quick entry for doctypes with tree view --- .../doctype/cost_center/cost_center.json | 5 +- erpnext/assets/doctype/location/location.json | 565 +--------- .../customer_group/customer_group.json | 8 +- .../setup/doctype/item_group/item_group.json | 1001 ++++------------- .../doctype/sales_person/sales_person.json | 470 +------- .../setup/doctype/territory/territory.json | 614 +++------- .../stock/doctype/warehouse/warehouse.json | 7 +- 7 files changed, 459 insertions(+), 2211 deletions(-) diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index 5149be216b3..976f05ad637 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_copy": 1, "allow_import": 1, "allow_rename": 1, @@ -123,7 +124,8 @@ ], "icon": "fa fa-money", "idx": 1, - "modified": "2019-09-16 14:44:17.103548", + "links": [], + "modified": "2020-01-28 13:50:23.430434", "modified_by": "Administrator", "module": "Accounts", "name": "Cost Center", @@ -162,7 +164,6 @@ "role": "Purchase User" } ], - "quick_entry": 1, "search_fields": "parent_cost_center, is_group", "show_name_in_global_search": 1, "sort_field": "modified", diff --git a/erpnext/assets/doctype/location/location.json b/erpnext/assets/doctype/location/location.json index 5dc13062b26..5ecc72ab913 100644 --- a/erpnext/assets/doctype/location/location.json +++ b/erpnext/assets/doctype/location/location.json @@ -1,580 +1,146 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:location_name", - "beta": 0, "creation": "2018-05-07 12:49:22.595974", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "location_name", + "parent_location", + "cb_details", + "is_container", + "is_group", + "sb_location_details", + "latitude", + "longitude", + "cb_latlong", + "area", + "area_uom", + "sb_geolocation", + "location", + "tree_details", + "lft", + "rgt", + "old_parent" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "location_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": "Location Name", - "length": 0, "no_copy": 1, - "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": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "parent_location", "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": "Parent Location", - "length": 0, - "no_copy": 0, "options": "Location", - "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": 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": "cb_details", - "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, + "default": "0", "description": "Check if it is a hydroponic unit", "fieldname": "is_container", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Container", - "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": "Is Container" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "is_group", "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": "Is Group", - "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": "Is Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sb_location_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": "Location Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Location Details" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "parent_location.latitude", "fieldname": "latitude", "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": "Latitude", - "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": "Latitude" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "parent_location.longitude", "fieldname": "longitude", "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": "Longitude", - "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": "Longitude" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "cb_latlong", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "area", "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": "Area", - "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, "depends_on": "eval:doc.area", "fieldname": "area_uom", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Area UOM", - "length": 0, - "no_copy": 0, - "options": "UOM", - "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": "UOM" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sb_geolocation", - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "location", "fieldtype": "Geolocation", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Location", - "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": "Location" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "tree_details", "fieldtype": "Section Break", "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": "Tree Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Tree Details" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "lft", "fieldtype": "Int", "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": "lft", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "rgt", "fieldtype": "Int", "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": "rgt", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "old_parent", "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": "Old Parent", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-11 13:36:30.999405", + "links": [], + "modified": "2020-01-28 13:52:22.513425", "modified_by": "Administrator", "module": "Assets", "name": "Location", @@ -582,127 +148,78 @@ "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "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": "Stock User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "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": "Accounts User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "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": "Stock Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "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": "Agriculture Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 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": "Agriculture User", - "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": 1, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/setup/doctype/customer_group/customer_group.json b/erpnext/setup/doctype/customer_group/customer_group.json index 3565b4b38a9..7fa242ae19d 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.json +++ b/erpnext/setup/doctype/customer_group/customer_group.json @@ -1,11 +1,12 @@ { - "_comments": "[]", + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:customer_group_name", "creation": "2013-01-10 16:34:23", "doctype": "DocType", "document_type": "Setup", + "engine": "InnoDB", "field_order": [ "customer_group_name", "parent_customer_group", @@ -136,7 +137,8 @@ ], "icon": "fa fa-sitemap", "idx": 1, - "modified": "2019-09-06 12:40:14.954697", + "links": [], + "modified": "2020-01-28 13:49:23.961708", "modified_by": "Administrator", "module": "Setup", "name": "Customer Group", @@ -187,8 +189,8 @@ "role": "Sales Manager" } ], - "quick_entry": 1, "search_fields": "parent_customer_group", "show_name_in_global_search": 1, + "sort_field": "modified", "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/setup/doctype/item_group/item_group.json b/erpnext/setup/doctype/item_group/item_group.json index 656d460e0f5..36e3e68ef43 100644 --- a/erpnext/setup/doctype/item_group/item_group.json +++ b/erpnext/setup/doctype/item_group/item_group.json @@ -1,817 +1,252 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:item_group_name", - "beta": 0, - "creation": "2013-03-28 10:35:29", - "custom": 0, - "description": "Item Classification", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:item_group_name", + "creation": "2013-03-28 10:35:29", + "description": "Item Classification", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "gs", + "item_group_name", + "parent_item_group", + "is_group", + "image", + "column_break_5", + "defaults", + "item_group_defaults", + "sec_break_taxes", + "taxes", + "sb9", + "show_in_website", + "route", + "weightage", + "slideshow", + "description", + "website_specifications", + "lft", + "rgt", + "old_parent" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "gs", - "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": "General Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "gs", + "fieldtype": "Section Break", + "label": "General Settings" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_group_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": "Item Group Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "item_group_name", - "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, + "fieldname": "item_group_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Group Name", + "oldfieldname": "item_group_name", + "oldfieldtype": "Data", + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "parent_item_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Parent Item Group", - "length": 0, - "no_copy": 0, - "oldfieldname": "parent_item_group", - "oldfieldtype": "Link", - "options": "Item Group", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "fieldname": "parent_item_group", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Parent Item Group", + "oldfieldname": "parent_item_group", + "oldfieldtype": "Link", + "options": "Item Group" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "description": "Only leaf nodes are allowed in transaction", - "fieldname": "is_group", - "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": "Is Group", - "length": 0, - "no_copy": 0, - "oldfieldname": "is_group", - "oldfieldtype": "Select", - "options": "", - "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 - }, + "bold": 1, + "default": "0", + "description": "Only leaf nodes are allowed in transaction", + "fieldname": "is_group", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Group", + "oldfieldname": "is_group", + "oldfieldtype": "Select" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "image", - "fieldtype": "Attach Image", - "hidden": 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": "Image", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "image", + "fieldtype": "Attach Image", + "hidden": 1, + "label": "Image" + }, { - "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, - "print_hide": 0, - "print_hide_if_no_value": 0, - "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_5", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "defaults", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Defaults", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "defaults", + "fieldtype": "Section Break", + "label": "Defaults" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_group_defaults", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Group Defaults", - "length": 0, - "no_copy": 0, - "options": "Item Default", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_group_defaults", + "fieldtype": "Table", + "label": "Item Group Defaults", + "options": "Item Default" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "fieldname": "sec_break_taxes", - "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": "Item Tax", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sec_break_taxes", + "fieldtype": "Section Break", + "label": "Item Tax" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "taxes", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Taxes", - "length": 0, - "no_copy": 0, - "options": "Item Tax", - "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": "taxes", + "fieldtype": "Table", + "label": "Taxes", + "options": "Item Tax" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb9", - "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": "Website Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sb9", + "fieldtype": "Section Break", + "label": "Website Settings" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Check this if you want to show in website", - "fieldname": "show_in_website", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show in Website", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "description": "Check this if you want to show in website", + "fieldname": "show_in_website", + "fieldtype": "Check", + "label": "Show in Website" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "show_in_website", - "fieldname": "route", - "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": "Route", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "depends_on": "show_in_website", + "fieldname": "route", + "fieldtype": "Data", + "label": "Route", "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "weightage", - "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": "Weightage", - "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": "weightage", + "fieldtype": "Int", + "label": "Weightage" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "show_in_website", - "description": "Show this slideshow at the top of the page", - "fieldname": "slideshow", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Slideshow", - "length": 0, - "no_copy": 0, - "options": "Website Slideshow", - "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 - }, + "depends_on": "show_in_website", + "description": "Show this slideshow at the top of the page", + "fieldname": "slideshow", + "fieldtype": "Link", + "label": "Slideshow", + "options": "Website Slideshow" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "show_in_website", - "description": "HTML / Banner that will show on the top of product list.", - "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, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "show_in_website", + "description": "HTML / Banner that will show on the top of product list.", + "fieldname": "description", + "fieldtype": "Text Editor", + "label": "Description" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "show_in_website", - "fieldname": "website_specifications", - "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": "Website Specifications", - "length": 0, - "no_copy": 0, - "options": "Item Website Specification", - "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 - }, + "depends_on": "show_in_website", + "fieldname": "website_specifications", + "fieldtype": "Table", + "label": "Website Specifications", + "options": "Item Website Specification" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lft", - "fieldtype": "Int", - "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": "lft", - "length": 0, - "no_copy": 1, - "oldfieldname": "lft", - "oldfieldtype": "Int", - "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 - }, + "fieldname": "lft", + "fieldtype": "Int", + "hidden": 1, + "label": "lft", + "no_copy": 1, + "oldfieldname": "lft", + "oldfieldtype": "Int", + "print_hide": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rgt", - "fieldtype": "Int", - "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": "rgt", - "length": 0, - "no_copy": 1, - "oldfieldname": "rgt", - "oldfieldtype": "Int", - "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 - }, + "fieldname": "rgt", + "fieldtype": "Int", + "hidden": 1, + "label": "rgt", + "no_copy": 1, + "oldfieldname": "rgt", + "oldfieldtype": "Int", + "print_hide": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "old_parent", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "old_parent", - "length": 0, - "no_copy": 1, - "oldfieldname": "old_parent", - "oldfieldtype": "Data", - "options": "Item Group", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "old_parent", + "fieldtype": "Link", + "hidden": 1, + "ignore_user_permissions": 1, + "label": "old_parent", + "no_copy": 1, + "oldfieldname": "old_parent", + "oldfieldtype": "Data", + "options": "Item Group", + "print_hide": 1, + "report_hide": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-sitemap", - "idx": 1, - "image_field": "image", - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 3, - "modified": "2018-11-23 15:17:28.003933", - "modified_by": "Administrator", - "module": "Setup", - "name": "Item Group", - "name_case": "Title Case", - "owner": "Administrator", + ], + "icon": "fa fa-sitemap", + "idx": 1, + "image_field": "image", + "links": [], + "max_attachments": 3, + "modified": "2020-01-28 13:51:05.456014", + "modified_by": "Administrator", + "module": "Setup", + "name": "Item Group", + "name_case": "Title Case", + "owner": "Administrator", "permissions": [ { - "amend": 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 Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User" + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Item Manager", - "set_user_permissions": 1, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Item Manager", + "set_user_permissions": 1, + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User" } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "parent_item_group", - "show_name_in_global_search": 1, - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 -} + ], + "search_fields": "parent_item_group", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/setup/doctype/sales_person/sales_person.json b/erpnext/setup/doctype/sales_person/sales_person.json index 27f79eb41f2..b05365d2ca9 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.json +++ b/erpnext/setup/doctype/sales_person/sales_person.json @@ -1,579 +1,183 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:sales_person_name", - "beta": 0, "creation": "2013-01-10 16:34:24", - "custom": 0, "description": "All Sales Transactions can be tagged against multiple **Sales Persons** so that you can set and monitor targets.", - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "name_and_employee_id", + "sales_person_name", + "parent_sales_person", + "commission_rate", + "is_group", + "enabled", + "cb0", + "employee", + "department", + "lft", + "rgt", + "old_parent", + "target_details_section_break", + "targets" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "name_and_employee_id", "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": "Name and Employee ID", - "length": 0, - "no_copy": 0, - "options": "icon-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": "icon-user" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "sales_person_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": "Sales Person Name", - "length": 0, - "no_copy": 0, "oldfieldname": "sales_person_name", "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, "description": "Select company name first.", - "fetch_if_empty": 0, "fieldname": "parent_sales_person", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Parent Sales Person", - "length": 0, - "no_copy": 0, "oldfieldname": "parent_sales_person", "oldfieldtype": "Link", - "options": "Sales Person", - "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": "Sales Person" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "commission_rate", "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": "Commission Rate", - "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 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "is_group", "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": "Is Group", - "length": 0, - "no_copy": 0, "oldfieldname": "is_group", "oldfieldtype": "Select", - "options": "", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", - "fetch_if_empty": 0, "fieldname": "enabled", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Enabled", - "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": "Enabled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "cb0", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "employee", "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": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "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": "Employee" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "employee.department", - "fetch_if_empty": 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": 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, - "fetch_if_empty": 0, "fieldname": "lft", "fieldtype": "Int", "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": "lft", - "length": 0, "no_copy": 1, "oldfieldname": "lft", "oldfieldtype": "Int", - "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, - "fetch_if_empty": 0, "fieldname": "rgt", "fieldtype": "Int", "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": "rgt", - "length": 0, "no_copy": 1, "oldfieldname": "rgt", "oldfieldtype": "Int", - "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, - "fetch_if_empty": 0, "fieldname": "old_parent", "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": "old_parent", - "length": 0, "no_copy": 1, "oldfieldname": "old_parent", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Set targets Item Group-wise for this Sales Person.", - "fetch_if_empty": 0, "fieldname": "target_details_section_break", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Sales Person Targets", - "length": 0, - "no_copy": 0, "oldfieldtype": "Section Break", - "options": "icon-bullseye", - "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": "icon-bullseye" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "targets", "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": "Targets", - "length": 0, - "no_copy": 0, "oldfieldname": "target_details", "oldfieldtype": "Table", - "options": "Target Detail", - "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": "Target Detail" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "icon-user", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-03-22 16:26:01.706129", + "links": [], + "modified": "2020-01-28 13:50:31.891050", "modified_by": "Administrator", "module": "Setup", "name": "Sales Person", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales Manager" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales 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": "Sales Master Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "search_fields": "parent_sales_person", "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/territory/territory.json b/erpnext/setup/doctype/territory/territory.json index beadb483779..91a3dda2bb0 100644 --- a/erpnext/setup/doctype/territory/territory.json +++ b/erpnext/setup/doctype/territory/territory.json @@ -1,487 +1,173 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:territory_name", - "beta": 0, - "creation": "2013-01-10 16:34:24", - "custom": 0, - "description": "Classification of Customers by region", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:territory_name", + "creation": "2013-01-10 16:34:24", + "description": "Classification of Customers by region", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "territory_name", + "parent_territory", + "is_group", + "cb0", + "territory_manager", + "lft", + "rgt", + "old_parent", + "target_details_section_break", + "targets" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "territory_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": "Territory Name", - "length": 0, - "no_copy": 1, - "oldfieldname": "territory_name", - "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, + "fieldname": "territory_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Territory Name", + "no_copy": 1, + "oldfieldname": "territory_name", + "oldfieldtype": "Data", + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, - "fieldname": "parent_territory", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Parent Territory", - "length": 0, - "no_copy": 0, - "oldfieldname": "parent_territory", - "oldfieldtype": "Link", - "options": "Territory", - "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 - }, + "bold": 1, + "fieldname": "parent_territory", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Parent Territory", + "oldfieldname": "parent_territory", + "oldfieldtype": "Link", + "options": "Territory" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, - "fieldname": "is_group", - "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": "Is Group", - "length": 0, - "no_copy": 0, - "oldfieldname": "is_group", - "oldfieldtype": "Select", - "options": "", - "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 - }, + "bold": 1, + "default": "0", + "fieldname": "is_group", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Group", + "oldfieldname": "is_group", + "oldfieldtype": "Select" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "cb0", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "cb0", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "For reference", - "fetch_if_empty": 0, - "fieldname": "territory_manager", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Territory Manager", - "length": 0, - "no_copy": 0, - "oldfieldname": "territory_manager", - "oldfieldtype": "Link", - "options": "Sales Person", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "For reference", + "fieldname": "territory_manager", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Territory Manager", + "oldfieldname": "territory_manager", + "oldfieldtype": "Link", + "options": "Sales Person", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "lft", - "fieldtype": "Int", - "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": "lft", - "length": 0, - "no_copy": 1, - "oldfieldname": "lft", - "oldfieldtype": "Int", - "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 - }, + "fieldname": "lft", + "fieldtype": "Int", + "hidden": 1, + "label": "lft", + "no_copy": 1, + "oldfieldname": "lft", + "oldfieldtype": "Int", + "print_hide": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "rgt", - "fieldtype": "Int", - "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": "rgt", - "length": 0, - "no_copy": 1, - "oldfieldname": "rgt", - "oldfieldtype": "Int", - "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 - }, + "fieldname": "rgt", + "fieldtype": "Int", + "hidden": 1, + "label": "rgt", + "no_copy": 1, + "oldfieldname": "rgt", + "oldfieldtype": "Int", + "print_hide": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, - "fieldname": "old_parent", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "old_parent", - "length": 0, - "no_copy": 1, - "oldfieldname": "old_parent", - "oldfieldtype": "Data", - "options": "Territory", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "old_parent", + "fieldtype": "Link", + "hidden": 1, + "ignore_user_permissions": 1, + "label": "old_parent", + "no_copy": 1, + "oldfieldname": "old_parent", + "oldfieldtype": "Data", + "options": "Territory", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Set Item Group-wise budgets on this Territory. You can also include seasonality by setting the Distribution.", - "fetch_if_empty": 0, - "fieldname": "target_details_section_break", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Territory Targets", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section 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 - }, + "description": "Set Item Group-wise budgets on this Territory. You can also include seasonality by setting the Distribution.", + "fieldname": "target_details_section_break", + "fieldtype": "Section Break", + "label": "Territory Targets", + "oldfieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "targets", - "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": "Targets", - "length": 0, - "no_copy": 0, - "oldfieldname": "target_details", - "oldfieldtype": "Table", - "options": "Target Detail", - "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": "targets", + "fieldtype": "Table", + "label": "Targets", + "oldfieldname": "target_details", + "oldfieldtype": "Table", + "options": "Target Detail" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-map-marker", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-03-21 16:26:58.581431", - "modified_by": "Administrator", - "module": "Setup", - "name": "Territory", - "name_case": "Title Case", - "owner": "Administrator", + ], + "icon": "fa fa-map-marker", + "idx": 1, + "links": [], + "modified": "2020-01-28 13:49:31.905800", + "modified_by": "Administrator", + "module": "Setup", + "name": "Territory", + "name_case": "Title Case", + "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": 1, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Master Manager", + "set_user_permissions": 1, + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "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": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "read": 1, + "role": "Stock 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": "Maintenance User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "read": 1, + "role": "Maintenance User" } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "parent_territory,territory_manager", - "show_name_in_global_search": 1, - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "search_fields": "parent_territory,territory_manager", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json index 646725956dc..2bf1ed89838 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.json +++ b/erpnext/stock/doctype/warehouse/warehouse.json @@ -1,10 +1,12 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "creation": "2013-03-07 18:50:32", "description": "A logical Warehouse against which stock entries are made.", "doctype": "DocType", "document_type": "Setup", + "engine": "InnoDB", "field_order": [ "warehouse_detail", "warehouse_name", @@ -226,7 +228,8 @@ ], "icon": "fa fa-building", "idx": 1, - "modified": "2019-05-22 11:17:23.357490", + "links": [], + "modified": "2020-01-28 13:50:59.368846", "modified_by": "Administrator", "module": "Stock", "name": "Warehouse", @@ -279,8 +282,8 @@ "role": "Manufacturing User" } ], - "quick_entry": 1, "show_name_in_global_search": 1, + "sort_field": "modified", "sort_order": "DESC", "title_field": "warehouse_name" } \ No newline at end of file From ade2c36123def1ec8f01aa1487bea36405de83ac Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 3 Feb 2020 18:54:35 +0530 Subject: [PATCH 085/929] fix: Unable to submit landed cost voucher (#20494) * fix: Unable to submit landed cost voucher * fix: Test case for multiple landed cost voucher against a Purchase receipt * fix: Test Case --- .../test_landed_cost_voucher.py | 70 +++++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 17 ++--- 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py index 988cf52ed05..62d369cb9d9 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -162,6 +162,76 @@ class TestLandedCostVoucher(unittest.TestCase): self.assertEqual(lcv.items[0].applicable_charges, 41.07) self.assertEqual(lcv.items[2].applicable_charges, 41.08) + def test_multiple_landed_cost_voucher_against_pr(self): + pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", + supplier_warehouse = "Stores - TCP1", do_not_save=True) + + pr.append("items", { + "item_code": "_Test Item", + "warehouse": "Stores - TCP1", + "cost_center": "Main - TCP1", + "qty": 5, + "rate": 100 + }) + + pr.submit() + + lcv1 = make_landed_cost_voucher(receipt_document_type = 'Purchase Receipt', + receipt_document=pr.name, charges=100, do_not_save=True) + + lcv1.insert() + lcv1.set('items', [ + lcv1.get('items')[0] + ]) + distribute_landed_cost_on_items(lcv1) + + lcv1.submit() + + lcv2 = make_landed_cost_voucher(receipt_document_type = 'Purchase Receipt', + receipt_document=pr.name, charges=100, do_not_save=True) + + lcv2.insert() + lcv2.set('items', [ + lcv2.get('items')[1] + ]) + distribute_landed_cost_on_items(lcv2) + + lcv2.submit() + + pr.load_from_db() + + self.assertEqual(pr.items[0].landed_cost_voucher_amount, 100) + self.assertEqual(pr.items[1].landed_cost_voucher_amount, 100) + +def make_landed_cost_voucher(** args): + args = frappe._dict(args) + ref_doc = frappe.get_doc(args.receipt_document_type, args.receipt_document) + + lcv = frappe.new_doc('Landed Cost Voucher') + lcv.company = '_Test Company' + lcv.distribute_charges_based_on = 'Amount' + + lcv.set('purchase_receipts', [{ + "receipt_document_type": args.receipt_document_type, + "receipt_document": args.receipt_document, + "supplier": ref_doc.supplier, + "posting_date": ref_doc.posting_date, + "grand_total": ref_doc.grand_total + }]) + + lcv.set("taxes", [{ + "description": "Shipping Charges", + "expense_account": "Expenses Included In Valuation - TCP1", + "amount": args.charges + }]) + + if not args.do_not_save: + lcv.insert() + if not args.do_not_submit: + lcv.submit() + + return lcv + def submit_landed_cost_voucher(receipt_document_type, receipt_document, charges=50): ref_doc = frappe.get_doc(receipt_document_type, receipt_document) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 09adb567513..fb123b9c1fa 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -349,7 +349,7 @@ class PurchaseReceipt(BuyingController): if warehouse_with_no_account: frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" + "\n".join(warehouse_with_no_account)) - + return process_gl_map(gl_entries) def get_asset_gl_entry(self, gl_entries): @@ -616,23 +616,16 @@ def get_item_account_wise_additional_cost(purchase_document): if not landed_cost_vouchers: return - - total_item_cost = 0 + item_account_wise_cost = {} - item_cost_allocated = [] for lcv in landed_cost_vouchers: - landed_cost_voucher_doc = frappe.get_cached_doc("Landed Cost Voucher", lcv.parent) + landed_cost_voucher_doc = frappe.get_doc("Landed Cost Voucher", lcv.parent) based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on) + total_item_cost = 0 for item in landed_cost_voucher_doc.items: - if item.purchase_receipt_item not in item_cost_allocated: - total_item_cost += item.get(based_on_field) - item_cost_allocated.append(item.purchase_receipt_item) - - for lcv in landed_cost_vouchers: - landed_cost_voucher_doc = frappe.get_cached_doc("Landed Cost Voucher", lcv.parent) - based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on) + total_item_cost += item.get(based_on_field) for item in landed_cost_voucher_doc.items: if item.receipt_document == purchase_document: From 0f90b870fb7d77a4ad68485f8ace85508860f2c6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 5 Feb 2020 12:12:47 +0530 Subject: [PATCH 086/929] fix: SQL query in financial statements --- .../accounts/report/financial_statements.py | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 57e8e0ea8fc..75433a135f3 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -348,40 +348,42 @@ def set_gl_entries_by_account( additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters) accounts = frappe.db.sql_list("""select name from `tabAccount` - where lft >= %s and rgt <= %s""", (root_lft, root_rgt)) - additional_conditions += " and account in ({})"\ - .format(", ".join([frappe.db.escape(d) for d in accounts])) + where lft >= %s and rgt <= %s and company = %s""", (root_lft, root_rgt, company)) - gl_filters = { - "company": company, - "from_date": from_date, - "to_date": to_date, - "finance_book": cstr(filters.get("finance_book")) - } + if accounts: + additional_conditions += " and account in ({})"\ + .format(", ".join([frappe.db.escape(d) for d in accounts])) - if filters.get("include_default_book_entries"): - gl_filters["company_fb"] = frappe.db.get_value("Company", - company, 'default_finance_book') + gl_filters = { + "company": company, + "from_date": from_date, + "to_date": to_date, + "finance_book": cstr(filters.get("finance_book")) + } - for key, value in filters.items(): - if value: - gl_filters.update({ - key: value - }) + if filters.get("include_default_book_entries"): + gl_filters["company_fb"] = frappe.db.get_value("Company", + company, 'default_finance_book') - gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry` - where company=%(company)s - {additional_conditions} - and posting_date <= %(to_date)s - order by account, posting_date""".format(additional_conditions=additional_conditions), gl_filters, as_dict=True) #nosec + for key, value in filters.items(): + if value: + gl_filters.update({ + key: value + }) - if filters and filters.get('presentation_currency'): - convert_to_presentation_currency(gl_entries, get_currency(filters)) + gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry` + where company=%(company)s + {additional_conditions} + and posting_date <= %(to_date)s + order by account, posting_date""".format(additional_conditions=additional_conditions), gl_filters, as_dict=True) #nosec - for entry in gl_entries: - gl_entries_by_account.setdefault(entry.account, []).append(entry) + if filters and filters.get('presentation_currency'): + convert_to_presentation_currency(gl_entries, get_currency(filters)) - return gl_entries_by_account + for entry in gl_entries: + gl_entries_by_account.setdefault(entry.account, []).append(entry) + + return gl_entries_by_account def get_additional_conditions(from_date, ignore_closing_entries, filters): From ce598530dbf04ac0fb51b46a9d508e0ad2c30c5c Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 5 Feb 2020 12:45:43 +0530 Subject: [PATCH 087/929] fix: requested changes --- erpnext/hr/doctype/vehicle_log/vehicle_log.js | 14 +++++++------- erpnext/hr/doctype/vehicle_log/vehicle_log.py | 11 ++++++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.js b/erpnext/hr/doctype/vehicle_log/vehicle_log.js index 4c192a0234b..bdb37d2b734 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.js +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.js @@ -5,32 +5,32 @@ frappe.ui.form.on("Vehicle Log", { refresh: function(frm) { if(frm.doc.license_plate && frm.doc.__islocal){ - frm.events.set_vehicle_details(frm) + frm.events.set_vehicle_details(frm); } if(frm.doc.docstatus == 1) { frm.add_custom_button(__('Expense Claim'), function() { - frm.events.expense_claim(frm) + frm.events.expense_claim(frm); }, __('Create')); frm.page.set_inner_btn_group_as_primary(__('Create')); } }, - license_plate: function(frm){ + license_plate: function(frm) { if(frm.doc.license_plate){ - frm.events.set_vehicle_details(frm) + frm.events.set_vehicle_details(frm); } }, - set_vehicle_details: function(frm){ + set_vehicle_details: function(frm) { frappe.call({ method: "erpnext.hr.doctype.vehicle_log.vehicle_log.get_make_model", args: { license_plate: frm.doc.license_plate }, callback: function(r) { - frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "model", r.message[0]); - frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "make", r.message[1]); + frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "make", r.message[0]); + frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "model", r.message[1]); frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "last_odometer", r.message[2]); frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "employee", r.message[3]); } diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.py b/erpnext/hr/doctype/vehicle_log/vehicle_log.py index 0dbf1ceb961..dfdfc0d7202 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.py @@ -18,12 +18,17 @@ class VehicleLog(Document): if not (service_detail.service_item and service_detail.type and service_detail.frequency and service_detail.expense_amount): frappe.throw(_("Service Item,Type,frequency and expense amount are required")) + def before_save(self): + model_details = get_make_model(self.license_plate) + self.make = model_details[0] + self.model = model_details[1] + self.last_odometer = model_details[2] + self.employee = model_details[3] + def on_submit(self): - print("I am here") frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", self.odometer) def on_cancel(self): - print("sel"*10, self.last_odometer, self.odometer) distance_travelled = self.odometer - self.last_odometer if(distance_travelled > 0): updated_odometer_value = int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled @@ -32,7 +37,7 @@ class VehicleLog(Document): @frappe.whitelist() def get_make_model(license_plate): vehicle=frappe.get_doc("Vehicle",license_plate) - return (vehicle.make,vehicle.model,vehicle.last_odometer,vehicle.employee) + return (vehicle.make, vehicle.model, vehicle.last_odometer, vehicle.employee) @frappe.whitelist() def make_expense_claim(docname): From b4a0c773ae5c8b6ec37edc64073175f5863981c1 Mon Sep 17 00:00:00 2001 From: "Parth J. Kharwar" Date: Wed, 5 Feb 2020 15:00:58 +0530 Subject: [PATCH 088/929] fix: half day leave date value reset (#20488) * fix: full day leaves not tagged as half day in attendance * chore: code cleanup for half day date value set --- .../hr/doctype/leave_application/leave_application.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 174641048b8..df4cf71b776 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -104,11 +104,16 @@ frappe.ui.form.on("Leave Application", { }, half_day: function(frm) { - if (frm.doc.from_date == frm.doc.to_date) { - frm.set_value("half_day_date", frm.doc.from_date); + if (frm.doc.half_day) { + if (frm.doc.from_date == frm.doc.to_date) { + frm.set_value("half_day_date", frm.doc.from_date); + } + else { + frm.trigger("half_day_datepicker"); + } } else { - frm.trigger("half_day_datepicker"); + frm.set_value("half_day_date", ""); } frm.trigger("calculate_total_days"); }, From 67dbeee7b28fd1f1fc8a349f4c1a01f2340c4e2c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 5 Feb 2020 09:38:05 +0530 Subject: [PATCH 089/929] fix: Add total row --- .../item_wise_purchase_register.py | 12 +++++--- .../item_wise_sales_register.py | 28 +++++++++++++++---- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 3a5a4548f5a..8b6359c134d 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -6,7 +6,7 @@ import frappe, erpnext from frappe import _ from frappe.utils import flt from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (get_tax_accounts, - get_grand_total, add_total_row, get_display_value, get_group_by_and_display_fields, update_total_row, + get_grand_total, add_total_row, get_display_value, get_group_by_and_display_fields, add_sub_total_row, get_group_by_conditions) def execute(filters=None): @@ -29,6 +29,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum data = [] total_row_map = {} + skip_total_row = 0 prev_group_by_value = '' if filters.get('group_by'): @@ -96,8 +97,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum row.update({'percent_gt': flt(row['total']/grand_total) * 100}) group_by_field, subtotal_display_field = get_group_by_and_display_fields(filters) data, prev_group_by_value = add_total_row(data, filters, prev_group_by_value, d, total_row_map, - group_by_field, subtotal_display_field, grand_total) - update_total_row(row, total_row_map, d.get(group_by_field, ''), tax_columns) + group_by_field, subtotal_display_field, grand_total, tax_columns) + add_sub_total_row(row, total_row_map, d.get(group_by_field, ''), tax_columns) data.append(row) @@ -106,8 +107,11 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum total_row['percent_gt'] = flt(total_row['total']/grand_total * 100) data.append(total_row) data.append({}) + add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns) + data.append(total_row_map.get('total_row')) + skip_total_row = 1 - return columns, data + return columns, data, None, None, None, skip_total_row def get_columns(additional_table_columns, filters): diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index dc900f09e36..ad2e12f8239 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -28,6 +28,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum data = [] total_row_map = {} + skip_total_row = 0 prev_group_by_value = '' if filters.get('group_by'): @@ -105,7 +106,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum row.update({'percent_gt': flt(row['total']/grand_total) * 100}) group_by_field, subtotal_display_field = get_group_by_and_display_fields(filters) data, prev_group_by_value = add_total_row(data, filters, prev_group_by_value, d, total_row_map, - group_by_field, subtotal_display_field, grand_total) + group_by_field, subtotal_display_field, grand_total, tax_columns) add_sub_total_row(row, total_row_map, d.get(group_by_field, ''), tax_columns) data.append(row) @@ -115,8 +116,11 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum total_row['percent_gt'] = flt(total_row['total']/grand_total * 100) data.append(total_row) data.append({}) - - return columns, data + add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns) + data.append(total_row_map.get('total_row')) + skip_total_row = 1 + + return columns, data, None, None, None, skip_total_row def get_columns(additional_table_columns, filters): columns = [] @@ -541,13 +545,13 @@ def get_tax_accounts(item_list, columns, company_currency, return itemised_tax, tax_columns def add_total_row(data, filters, prev_group_by_value, item, total_row_map, - group_by_field, subtotal_display_field, grand_total): + group_by_field, subtotal_display_field, grand_total, tax_columns): if prev_group_by_value != item.get(group_by_field, ''): if prev_group_by_value: total_row = total_row_map.get(prev_group_by_value) - total_row['percent_gt'] = flt(total_row['total']/grand_total * 100) data.append(total_row) data.append({}) + add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns) prev_group_by_value = item.get(group_by_field, '') @@ -557,7 +561,18 @@ def add_total_row(data, filters, prev_group_by_value, item, total_row_map, 'amount': 0.0, 'bold': 1, 'total_tax': 0.0, - 'total': 0.0 + 'total': 0.0, + 'percent_gt': 0.0 + }) + + total_row_map.setdefault('total_row', { + subtotal_display_field: "Total", + 'stock_qty': 0.0, + 'amount': 0.0, + 'bold': 1, + 'total_tax': 0.0, + 'total': 0.0, + 'percent_gt': 0.0 }) return data, prev_group_by_value @@ -600,6 +615,7 @@ def add_sub_total_row(item, total_row_map, group_by_value, tax_columns): total_row['amount'] += item['amount'] total_row['total_tax'] += item['total_tax'] total_row['total'] += item['total'] + total_row['percent_gt'] += item['percent_gt'] for tax in tax_columns: total_row.setdefault(frappe.scrub(tax + ' Amount'), 0.0) From 8e0a9e8748665aabf45105732bf70de4e1807192 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 5 Feb 2020 16:17:16 +0530 Subject: [PATCH 090/929] fix: unsupported operand type(s) for += 'int' and 'NoneType' --- erpnext/stock/doctype/pick_list/pick_list.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index c4d8c41ebb7..7ed998f979a 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -74,13 +74,13 @@ class PickList(Document): if item_map.get(key): item_map[key].qty += item.qty - item_map[key].stock_qty += item.stock_qty + item_map[key].stock_qty += flt(item.stock_qty) else: item_map[key] = item # maintain count of each item (useful to limit get query) self.item_count_map.setdefault(item_code, 0) - self.item_count_map[item_code] += item.stock_qty + self.item_count_map[item_code] += flt(item.stock_qty) return item_map.values() From 9f58e59a2c8a3ab723bd9f2973ed31ac9e80fe72 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Wed, 29 Jan 2020 21:41:56 +0530 Subject: [PATCH 091/929] fix: Label and UX fixes while creating payment entry against customer --- erpnext/accounts/doctype/bank_account/bank_account.json | 6 ++++-- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 +++- erpnext/selling/doctype/customer/customer.js | 8 ++++++++ erpnext/selling/doctype/customer/customer.json | 6 ++++-- erpnext/selling/doctype/customer/customer.py | 6 ++++++ 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json index 8e30b8555cb..c8ae26d9f2c 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.json +++ b/erpnext/accounts/doctype/bank_account/bank_account.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "creation": "2017-05-29 21:35:13.136357", @@ -82,7 +83,7 @@ "default": "0", "fieldname": "is_default", "fieldtype": "Check", - "label": "Is the Default Account" + "label": "Is Default Account" }, { "default": "0", @@ -211,7 +212,8 @@ "read_only": 1 } ], - "modified": "2019-10-02 01:34:12.417601", + "links": [], + "modified": "2020-01-29 20:42:26.458316", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Account", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 214d6088667..d65e5640263 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -102,7 +102,9 @@ class PaymentEntry(AccountsController): self.bank = bank_data.bank self.bank_account_no = bank_data.bank_account_no - self.set(field, bank_data.account) + + if not self.get(field): + self.set(field, bank_data.account) def validate_allocated_amount(self): for d in self.get("references"): diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 30220614e9b..0a9f9b8f0a7 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -55,6 +55,14 @@ frappe.ui.form.on("Customer", { } } }) + + frm.set_query('default_bank_account', function(c) { + return { + filters: { + 'is_company_account': 1 + } + } + }); }, customer_primary_address: function(frm){ if(frm.doc.customer_primary_address){ diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 250973b7497..89ce325a847 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_events_in_timeline": 1, "allow_import": 1, "allow_rename": 1, @@ -122,7 +123,7 @@ { "fieldname": "default_bank_account", "fieldtype": "Link", - "label": "Default Bank Account", + "label": "Default Company Bank Account", "options": "Bank Account" }, { @@ -470,7 +471,8 @@ "icon": "fa fa-user", "idx": 363, "image_field": "image", - "modified": "2020-01-24 15:06:03.483799", + "links": [], + "modified": "2020-01-29 20:36:37.879581", "modified_by": "Administrator", "module": "Selling", "name": "Customer", diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 6ee02190bad..3248c998b60 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -54,6 +54,7 @@ class Customer(TransactionBase): self.validate_credit_limit_on_change() self.set_loyalty_program() self.check_customer_group_change() + self.validate_default_bank_account() # set loyalty program tier if frappe.db.exists('Customer', self.name): @@ -72,6 +73,11 @@ class Customer(TransactionBase): if self.customer_group != frappe.db.get_value('Customer', self.name, 'customer_group'): frappe.flags.customer_group_changed = True + def validate_default_bank_account(self): + if self.default_bank_account: + is_company_account = frappe.db.get_value('Bank Account', self.default_bank_account, 'is_company_account') + frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account))) + def on_update(self): self.validate_name_with_customer_group() self.create_primary_contact() From 3ed574532de1e034637678b7a85490f94cab4c48 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Wed, 29 Jan 2020 21:45:17 +0530 Subject: [PATCH 092/929] fix: filter --- erpnext/selling/doctype/customer/customer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 0a9f9b8f0a7..6db4102a7bf 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -56,7 +56,7 @@ frappe.ui.form.on("Customer", { } }) - frm.set_query('default_bank_account', function(c) { + frm.set_query('default_bank_account', function() { return { filters: { 'is_company_account': 1 From d9f8347fc80455d41a44d62097f6ae91c226b47a Mon Sep 17 00:00:00 2001 From: thefalconx33 Date: Wed, 5 Feb 2020 16:40:58 +0530 Subject: [PATCH 093/929] fix: typo; serial no doesn't have amc start date --- .../doctype/maintenance_schedule/maintenance_schedule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index 94d85f77ef4..c5bc92dcc17 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -193,7 +193,7 @@ class MaintenanceSchedule(TransactionBase): if sr_details.amc_expiry_date and getdate(sr_details.amc_expiry_date) >= getdate(amc_start_date): throw(_("Serial No {0} is under maintenance contract upto {1}") - .format(serial_no, sr_details.amc_start_date)) + .format(serial_no, sr_details.amc_expiry_date)) if not sr_details.warehouse and sr_details.delivery_date and \ getdate(sr_details.delivery_date) >= getdate(amc_start_date): From 0eb9f7408e59a08e2cacca21259a156033868bad Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 6 Feb 2020 14:34:11 +0530 Subject: [PATCH 094/929] fix: Procurement Tracker report not working --- .../report/procurement_tracker/procurement_tracker.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index 48295bee26a..866bf0c7332 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -141,13 +141,13 @@ def get_conditions(filters): conditions = "" if filters.get("company"): - conditions += " AND company='%s'"% filters.get('company') + conditions += " AND company=%s"% frappe.db.escape(filters.get('company')) if filters.get("cost_center") or filters.get("project"): conditions += """ - AND (cost_center='%s' - OR project='%s') - """% (filters.get('cost_center'), filters.get('project')) + AND (cost_center=%s + OR project=%s) + """% (frappe.db.escape(filters.get('cost_center')), frappe.db.escape(filters.get('project'))) if filters.get("from_date"): conditions += " AND transaction_date>=%s"% filters.get('from_date') From bdfa2ff33ac56620964f24e0f058866a82413b4d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 7 Feb 2020 11:05:33 +0530 Subject: [PATCH 095/929] fix: Do not calculate total for rate --- .../report/item_wise_sales_register/item_wise_sales_register.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index dc900f09e36..524f949a334 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -603,8 +603,6 @@ def add_sub_total_row(item, total_row_map, group_by_value, tax_columns): for tax in tax_columns: total_row.setdefault(frappe.scrub(tax + ' Amount'), 0.0) - total_row.setdefault(frappe.scrub(tax + ' Rate'), 0.0) - total_row[frappe.scrub(tax + ' Rate')] += flt(item[frappe.scrub(tax + ' Rate')]) total_row[frappe.scrub(tax + ' Amount')] += flt(item[frappe.scrub(tax + ' Amount')]) From 7027584391979b25f84b4a504c44d60aa3226411 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 7 Feb 2020 14:38:25 +0530 Subject: [PATCH 096/929] fix: time out error while submitting the purchase receipt which has more than 100 serial nos --- .../purchase_receipt/test_purchase_receipt.py | 24 +++++++ erpnext/stock/doctype/serial_no/serial_no.py | 66 ++++++++++--------- 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index c80b9bd04bb..707750413e0 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -262,6 +262,30 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(pr2.per_billed, 80) self.assertEqual(pr2.status, "To Bill") + def test_serial_no_against_purchase_receipt(self): + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + + item_code = "Test Manual Created Serial No" + if not frappe.db.exists("Item", item_code): + item = make_item(item_code, dict(has_serial_no=1)) + + serial_no = random_string(5) + pr_doc = make_purchase_receipt(item_code=item_code, + qty=1, serial_no = serial_no) + + self.assertEqual(serial_no, frappe.db.get_value("Serial No", + {"purchase_document_type": "Purchase Receipt", "purchase_document_no": pr_doc.name}, "name")) + + item_code = "Test Auto Created Serial No" + if not frappe.db.exists("Item", item_code): + item = make_item(item_code, dict(has_serial_no=1, serial_no_series="KLJL.###")) + + new_pr_doc = make_purchase_receipt(item_code=item_code, qty=1) + + serial_no = get_serial_nos(new_pr_doc[0].serial_no)[0] + self.assertEqual(serial_no, frappe.db.get_value("Serial No", + {"purchase_document_type": "Purchase Receipt", "purchase_document_no": new_pr_doc.name}, "name")) + def test_not_accept_duplicate_serial_no(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 19eb398130c..855ab7a9662 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -29,13 +29,12 @@ class SerialNo(StockController): self.via_stock_ledger = False def validate(self): - if self.get("__islocal") and self.warehouse: + if self.get("__islocal") and self.warehouse and not self.via_stock_ledger: frappe.throw(_("New Serial No cannot have Warehouse. Warehouse must be set by Stock Entry or Purchase Receipt"), SerialNoCannotCreateDirectError) self.set_maintenance_status() self.validate_warehouse() self.validate_item() - self.on_stock_ledger_entry() def set_maintenance_status(self): if not self.warranty_expiry_date and not self.amc_expiry_date: @@ -68,7 +67,7 @@ class SerialNo(StockController): """ Validate whether serial no is required for this item """ - item = frappe.get_doc("Item", self.item_code) + item = frappe.get_cached_doc("Item", self.item_code) if item.has_serial_no!=1: frappe.throw(_("Item {0} is not setup for Serial Nos. Check Item master").format(self.item_code)) @@ -117,9 +116,9 @@ class SerialNo(StockController): "warranty_expiry_date"): self.set(fieldname, None) - def get_last_sle(self): + def get_last_sle(self, serial_no=None): entries = {} - sle_dict = self.get_stock_ledger_entries() + sle_dict = self.get_stock_ledger_entries(serial_no) if sle_dict: if sle_dict.get("incoming", []): entries["purchase_sle"] = sle_dict["incoming"][0] @@ -132,13 +131,28 @@ class SerialNo(StockController): return entries - def get_stock_ledger_entries(self): + def get_stock_ledger_entries(self, serial_no=None): sle_dict = {} - for sle in frappe.db.sql("""select * from `tabStock Ledger Entry` - where serial_no like %s and item_code=%s and ifnull(is_cancelled, 'No')='No' - order by posting_date desc, posting_time desc, creation desc""", - ("%%%s%%" % self.name, self.item_code), as_dict=1): - if self.name.upper() in get_serial_nos(sle.serial_no): + if not serial_no: + serial_no = self.name + + for sle in frappe.db.sql(""" + SELECT voucher_type, voucher_no, + posting_date, posting_time, incoming_rate, actual_qty, serial_no + FROM + `tabStock Ledger Entry` + WHERE + item_code=%s AND company = %s AND ifnull(is_cancelled, 'No')='No' + AND (serial_no = %s + OR serial_no like %s + OR serial_no like %s + OR serial_no like %s + ) + ORDER BY + posting_date desc, posting_time desc, creation desc""", + (self.item_code, self.company, + serial_no, serial_no+'\n%', '%\n'+serial_no, '%\n'+serial_no+'\n%'), as_dict=1): + if serial_no.upper() in get_serial_nos(sle.serial_no): if cint(sle.actual_qty) > 0: sle_dict.setdefault("incoming", []).append(sle) else: @@ -178,12 +192,11 @@ class SerialNo(StockController): where name=%s""" % (dt[0], '%s', '%s'), ('\n'.join(list(serial_nos)), item[0])) - def on_stock_ledger_entry(self): - if self.via_stock_ledger and not self.get("__islocal"): - last_sle = self.get_last_sle() - self.set_purchase_details(last_sle.get("purchase_sle")) - self.set_sales_details(last_sle.get("delivery_sle")) - self.set_maintenance_status() + def update_serial_no_reference(self, serial_no=None): + last_sle = self.get_last_sle(serial_no) + self.set_purchase_details(last_sle.get("purchase_sle")) + self.set_sales_details(last_sle.get("delivery_sle")) + self.set_maintenance_status() def process_serial_no(sle): item_det = get_item_details(sle.item_code) @@ -387,27 +400,16 @@ def get_serial_nos(serial_no): def make_serial_no(serial_no, args): sr = frappe.new_doc("Serial No") - sr.warehouse = None - sr.dont_update_if_missing.append("warehouse") - sr.flags.ignore_permissions = True sr.serial_no = serial_no sr.item_code = args.get('item_code') sr.company = args.get('company') sr.batch_no = args.get('batch_no') sr.via_stock_ledger = args.get('via_stock_ledger') or True - sr.asset = args.get('asset') - sr.location = args.get('location') - + sr.warehouse = args.get('warehouse') - if args.get('purchase_document_type'): - sr.purchase_document_type = args.get('purchase_document_type') - sr.purchase_document_no = args.get('purchase_document_no') - sr.supplier = args.get('supplier') - - sr.insert() - if args.get('warehouse'): - sr.warehouse = args.get('warehouse') - sr.save() + sr.validate_item() + sr.update_serial_no_reference(serial_no) + sr.db_insert() return sr.name From bbeacb9acf06ac319c9e30d2f2d3ba39cecb6903 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 7 Feb 2020 14:56:58 +0530 Subject: [PATCH 097/929] fix: incorrect warehouse for material request in production plan --- erpnext/manufacturing/doctype/production_plan/production_plan.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 10a8d8fc9bd..76202e5a6f3 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -625,7 +625,6 @@ def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None): for data in po_items: planned_qty = data.get('required_qty') or data.get('planned_qty') ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty') or ignore_existing_ordered_qty - warehouse = data.get("warehouse") or warehouse item_details = {} if data.get("bom") or data.get("bom_no"): From f5bff5f15d69b44ec86c8c39f539eb3e0c2dd055 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 7 Feb 2020 15:25:43 +0530 Subject: [PATCH 098/929] fix: Styling and minor fixes --- .../bank_reconciliation/bank_reconciliation.py | 6 +++--- .../doctype/plaid_settings/plaid_settings.json | 7 ++++--- .../v12_0/rename_bank_reconciliation_fields.py | 11 +++-------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 804bcd8bdf0..52bbe3327a8 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -35,7 +35,7 @@ class BankReconciliation(Document): and ifnull(t1.is_opening, 'No') = 'No' %(condition)s group by t2.account, t1.name order by t1.posting_date ASC, t1.name DESC - """, {"condition":condition, "account":self.account, "from":self.from_date, "to":self.to_date}, as_dict=1) + """, {"condition":condition, "account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) payment_entries = frappe.db.sql(""" select @@ -52,8 +52,8 @@ class BankReconciliation(Document): and bank_account = %(bank_account)s order by posting_date ASC, name DESC - """, {"account":self.account, "from":self.from_date, - "to":self.to_date, "bank_account": self.bank_account}, as_dict=1) + """, {"account": self.account, "from":self.from_date, + "to": self.to_date, "bank_account": self.bank_account}, as_dict=1) pos_entries = [] if self.include_pos_transactions: diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json index df77ad8bc93..d8203d7390f 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json @@ -49,9 +49,10 @@ }, { "fieldname": "plaid_env", - "fieldtype": "Data", + "fieldtype": "Select", "in_list_view": 1, - "label": "Plaid Environment" + "label": "Plaid Environment", + "options": "sandbox\ndevelopment\nproduction" }, { "fieldname": "column_break_2", @@ -69,7 +70,7 @@ ], "issingle": 1, "links": [], - "modified": "2020-01-05 10:00:22.137832", + "modified": "2020-02-07 15:21:11.616231", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Plaid Settings", diff --git a/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py b/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py index 01b1dde6073..caeda8ae61b 100644 --- a/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py +++ b/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py @@ -9,11 +9,6 @@ def _rename_single_field(**kwargs): frappe.db.sql("UPDATE tabSingles SET field='{new_name}' WHERE doctype='{doctype}' AND field='{old_name}';".format(**kwargs)) #nosec def execute(): - BR = "Bank Reconciliation" - AC = "account" - BA = "bank_account" - BAN = "bank_account_no" - - _rename_single_field(doctype = BR, old_name = BA , new_name = AC) - _rename_single_field(doctype = BR, old_name = BAN, new_name = BA) - frappe.reload_doc("Accounts", "doctype", BR) + _rename_single_field(doctype = "Bank Reconciliation", old_name = "bank_account" , new_name = "account") + _rename_single_field(doctype = "Bank Reconciliation", old_name = "bank_account_no", new_name = "bank_account") + frappe.reload_doc("Accounts", "doctype", "Bank Reconciliation") From a9205adfbdc42dcc10afa1737425f68483ca9357 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 7 Feb 2020 15:36:38 +0530 Subject: [PATCH 099/929] fix: test cases --- .../doctype/purchase_receipt/test_purchase_receipt.py | 11 +++++++---- erpnext/stock/doctype/serial_no/serial_no.py | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 707750413e0..aa09d3bfe5f 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -158,8 +158,11 @@ class TestPurchaseReceipt(unittest.TestCase): def test_purchase_return_for_rejected_qty(self): from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse - rejected_warehouse=get_warehouse(company = "_Test Company with perpetual inventory", abbr = " - TCP1", warehouse_name = "_Test Rejected Warehouse").name - print(rejected_warehouse) + rejected_warehouse="_Test Rejected Warehouse - TCP1" + if not frappe.db.exists("Warehouse", rejected_warehouse): + get_warehouse(company = "_Test Company with perpetual inventory", + abbr = " - TCP1", warehouse_name = "_Test Rejected Warehouse").name + pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", received_qty=4, qty=2, rejected_warehouse=rejected_warehouse) return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, received_qty = -4, qty=-2, rejected_warehouse=rejected_warehouse) @@ -269,7 +272,7 @@ class TestPurchaseReceipt(unittest.TestCase): if not frappe.db.exists("Item", item_code): item = make_item(item_code, dict(has_serial_no=1)) - serial_no = random_string(5) + serial_no = "12903812901" pr_doc = make_purchase_receipt(item_code=item_code, qty=1, serial_no = serial_no) @@ -282,7 +285,7 @@ class TestPurchaseReceipt(unittest.TestCase): new_pr_doc = make_purchase_receipt(item_code=item_code, qty=1) - serial_no = get_serial_nos(new_pr_doc[0].serial_no)[0] + serial_no = get_serial_nos(new_pr_doc.items[0].serial_no)[0] self.assertEqual(serial_no, frappe.db.get_value("Serial No", {"purchase_document_type": "Purchase Receipt", "purchase_document_no": new_pr_doc.name}, "name")) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 855ab7a9662..f34b0472f0d 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -379,6 +379,7 @@ def auto_make_serial_nos(args): if sr.sales_order and args.get('voucher_type') == "Stock Entry" \ and not args.get('actual_qty', 0) > 0: sr.sales_order = None + sr.update_serial_no_reference() sr.save(ignore_permissions=True) elif args.get('actual_qty', 0) > 0: created_numbers.append(make_serial_no(serial_no, args)) From c7e523cee437f7af7821ea893d8f1fc46ea323d7 Mon Sep 17 00:00:00 2001 From: thefalconx33 Date: Sun, 9 Feb 2020 12:19:03 +0530 Subject: [PATCH 100/929] fix: mandatory warehouse wrror while updating items after submission --- erpnext/controllers/accounts_controller.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 7faf792d203..001da5b5ea8 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1135,6 +1135,7 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.reqd_by_date = p_doctype.delivery_date child_item.uom = item.stock_uom child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.warehouse = p_doctype.set_warehouse or p_doctype.items[0].warehouse return child_item From ec45c096a08983e10e2f57bddab1eb14f192b773 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 9 Feb 2020 19:45:42 +0530 Subject: [PATCH 101/929] fix: pricing rule not working on item groups (#20546) --- .../doctype/pricing_rule/test_pricing_rule.py | 48 +++++++++++++++++++ .../accounts/doctype/pricing_rule/utils.py | 2 +- .../setup/doctype/item_group/item_group.py | 12 ++--- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 332a9007915..9c1fef69fac 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -9,6 +9,8 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.stock.get_item_details import get_item_details from frappe import MandatoryError +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.healthcare.doctype.lab_test_template.lab_test_template import make_item_price class TestPricingRule(unittest.TestCase): def setUp(self): @@ -145,6 +147,52 @@ class TestPricingRule(unittest.TestCase): self.assertEquals(details.get("margin_type"), "Percentage") self.assertEquals(details.get("margin_rate_or_amount"), 10) + def test_mixed_conditions_for_item_group(self): + for item in ["Mixed Cond Item 1", "Mixed Cond Item 2"]: + make_item(item, {"item_group": "Products"}) + make_item_price(item, "_Test Price List", 100) + + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule for Item Group", + "apply_on": "Item Group", + "item_groups": [ + { + "item_group": "Products", + }, + { + "item_group": "Seed", + }, + ], + "selling": 1, + "mixed_conditions": 1, + "currency": "USD", + "rate_or_discount": "Discount Percentage", + "discount_percentage": 10, + "applicable_for": "Customer Group", + "customer_group": "All Customer Groups", + "company": "_Test Company" + } + frappe.get_doc(test_record.copy()).insert() + + args = frappe._dict({ + "item_code": "Mixed Cond Item 1", + "item_group": "Products", + "company": "_Test Company", + "price_list": "_Test Price List", + "currency": "_Test Currency", + "doctype": "Sales Order", + "conversion_rate": 1, + "price_list_currency": "_Test Currency", + "plc_conversion_rate": 1, + "order_type": "Sales", + "customer": "_Test Customer", + "customer_group": "_Test Customer Group", + "name": None + }) + details = get_item_details(args) + self.assertEquals(details.get("discount_percentage"), 10) + def test_pricing_rule_for_variants(self): from erpnext.stock.get_item_details import get_item_details from frappe import MandatoryError diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index fe68fdb6c0f..e475563c77c 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -489,7 +489,7 @@ def get_pricing_rule_items(pr_doc): for d in pr_doc.get(pricing_rule_apply_on): if apply_on == 'item_group': - get_child_item_groups(d.get(apply_on)) + apply_on_data.extend(get_child_item_groups(d.get(apply_on))) else: apply_on_data.append(d.get(apply_on)) diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 9f258828660..43778404b60 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -174,15 +174,11 @@ def get_child_groups(item_group_name): and show_in_website = 1""", {"lft": item_group.lft, "rgt": item_group.rgt}) def get_child_item_groups(item_group_name): - child_item_groups = frappe.cache().hget("child_item_groups", item_group_name) + item_group = frappe.get_cached_value("Item Group", + item_group_name, ["lft", "rgt"], as_dict=1) - if not child_item_groups: - item_group = frappe.get_cached_doc("Item Group", item_group_name) - - child_item_groups = [d.name for d in frappe.get_all('Item Group', - filters= {'lft': ('>=', item_group.lft),'rgt': ('>=', item_group.rgt)})] - - frappe.cache().hset("child_item_groups", item_group_name, child_item_groups) + child_item_groups = [d.name for d in frappe.get_all('Item Group', + filters= {'lft': ('>=', item_group.lft),'rgt': ('<=', item_group.rgt)})] return child_item_groups or {} From 1343a4b7557266b2d8ec4d2b3b3c0498deb38ff5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 10 Feb 2020 00:31:44 +0530 Subject: [PATCH 102/929] fix: patch for creating irs_1099 custom field (United States) --- erpnext/patches.txt | 1 + .../v12_0/create_irs_1099_field_united_states.py | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 erpnext/patches/v12_0/create_irs_1099_field_united_states.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 17c9a20766d..9fde54225ac 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -648,3 +648,4 @@ erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger erpnext.patches.v12_0.update_price_or_product_discount erpnext.patches.v12_0.add_export_type_field_in_party_master +erpnext.patches.v12_0.create_irs_1099_field_united_states \ No newline at end of file diff --git a/erpnext/patches/v12_0/create_irs_1099_field_united_states.py b/erpnext/patches/v12_0/create_irs_1099_field_united_states.py new file mode 100644 index 00000000000..3e4c87f434e --- /dev/null +++ b/erpnext/patches/v12_0/create_irs_1099_field_united_states.py @@ -0,0 +1,10 @@ +from __future__ import unicode_literals +import frappe +from erpnext.regional.united_states.setup import make_custom_fields + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'United States'}) + if not company: + return + + make_custom_fields() \ No newline at end of file From 495ab6c7f241c7e6984ab516dc4ff4b31c5cb760 Mon Sep 17 00:00:00 2001 From: Ashish Shah Date: Mon, 10 Feb 2020 11:13:27 +0530 Subject: [PATCH 103/929] fix: backflush raw material based on - Material Transferred for Manufacture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hi @nabinhait , Issue : Manufacturing setting > Backflush Raw Material Based on “Material Transferred for Manufacture” doesn't fetch the actual raw material transferred qty. It fetches qty based on "BOM" The issue is because @creamdory in PR #https://github.com/frappe/erpnext/pull/13384 commit : https://github.com/frappe/erpnext/pull/13384/files#diff-91f0ed661ef4b6e1f167fc7961b1a79b ``` changed from: if trans_qty and manufacturing_qty >= (produced_qty + flt(self.fg_completed_qty)): to : if trans_qty and manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)): ``` **'='** was added by her in the condition, which was not there before her commit. Kindly except the fix for the issue. https://github.com/frappe/erpnext/blob/develop/erpnext/stock/doctype/stock_entry/stock_entry.py#L1057 https://github.com/frappe/erpnext/pull/13384 https://github.com/frappe/erpnext/pull/13384/files#diff-91f0ed661ef4b6e1f167fc7961b1a79b **before fix gif** : Stock Entry = Manufacture shows raw material quantity as per BOM. ![FetchTransQtyError](https://user-images.githubusercontent.com/29812965/74123824-ffdd8600-4bf5-11ea-8873-95de24a7ef09.gif) **after fix gi**f : Stock Entry = "Manufacture" shows raw material quantity as per "Material Transfer for Manufacture". ![FetchTransQtyFix](https://user-images.githubusercontent.com/29812965/74123836-0c61de80-4bf6-11ea-86fb-d9619fd9b02b.gif) --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 4b07f0aef9f..56cb3f06b94 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1056,7 +1056,7 @@ class StockEntry(StockController): req_qty_each = flt(req_qty / manufacturing_qty) consumed_qty = flt(req_items[0].consumed_qty) - if trans_qty and manufacturing_qty >= (produced_qty + flt(self.fg_completed_qty)): + if trans_qty and manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)): if qty >= req_qty: qty = (req_qty/trans_qty) * flt(self.fg_completed_qty) else: From a7c27596bb0607b749fcec1a2313405e01ddc674 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 10 Feb 2020 15:30:22 +0530 Subject: [PATCH 104/929] fix: added indexing on columns to fix performace issue --- .../accounts/doctype/gl_entry/gl_entry.json | 805 ++---------------- .../doctype/sales_invoice/sales_invoice.json | 7 +- 2 files changed, 63 insertions(+), 749 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json index a232a953f31..e64bc9e0c78 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.json +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json @@ -1,971 +1,286 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, "autoname": "ACC-GLE-.YYYY.-.#####", - "beta": 0, "creation": "2013-01-10 16:34:06", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Document", - "editable_grid": 0, + "engine": "InnoDB", + "field_order": [ + "posting_date", + "transaction_date", + "account", + "party_type", + "party", + "cost_center", + "debit", + "credit", + "account_currency", + "debit_in_account_currency", + "credit_in_account_currency", + "against", + "against_voucher_type", + "against_voucher", + "voucher_type", + "voucher_no", + "voucher_detail_no", + "project", + "remarks", + "is_opening", + "is_advance", + "fiscal_year", + "company", + "finance_book", + "to_rename", + "due_date" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "posting_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Posting Date", - "length": 0, - "no_copy": 0, "oldfieldname": "posting_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": 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": "transaction_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": "Transaction Date", - "length": 0, - "no_copy": 0, "oldfieldname": "transaction_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": "account", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Account", - "length": 0, - "no_copy": 0, "oldfieldname": "account", "oldfieldtype": "Link", "options": "Account", - "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": 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": "party_type", "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": "Party Type", - "length": 0, - "no_copy": 0, "options": "DocType", - "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": 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": "party", "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Party", - "length": 0, - "no_copy": 0, "options": "party_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": 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": "cost_center", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Cost Center", - "length": 0, - "no_copy": 0, "oldfieldname": "cost_center", "oldfieldtype": "Link", - "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": 0, - "columns": 0, "fieldname": "debit", "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": "Debit Amount", - "length": 0, - "no_copy": 0, "oldfieldname": "debit", "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Company:company:default_currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "credit", "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": "Credit Amount", - "length": 0, - "no_copy": 0, "oldfieldname": "credit", "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Company:company:default_currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "account_currency", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Account Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "debit_in_account_currency", "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": "Debit Amount in Account Currency", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "credit_in_account_currency", "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": "Credit Amount in Account Currency", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "against", "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Against", - "length": 0, - "no_copy": 0, "oldfieldname": "against", - "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 + "oldfieldtype": "Text" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "against_voucher_type", "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": "Against Voucher Type", - "length": 0, - "no_copy": 0, "oldfieldname": "against_voucher_type", "oldfieldtype": "Data", "options": "DocType", - "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 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "against_voucher", "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Against Voucher", - "length": 0, - "no_copy": 0, "oldfieldname": "against_voucher", "oldfieldtype": "Data", "options": "against_voucher_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": 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": "voucher_type", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Voucher Type", - "length": 0, - "no_copy": 0, "oldfieldname": "voucher_type", "oldfieldtype": "Select", "options": "DocType", - "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 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "voucher_no", "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Voucher No", - "length": 0, - "no_copy": 0, "oldfieldname": "voucher_no", "oldfieldtype": "Data", "options": "voucher_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": 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": "voucher_detail_no", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Voucher Detail No", - "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": "project", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Project", - "length": 0, - "no_copy": 0, - "options": "Project", - "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" }, { - "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": 1, - "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 + "oldfieldtype": "Text" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "is_opening", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Is Opening", - "length": 0, - "no_copy": 0, "oldfieldname": "is_opening", "oldfieldtype": "Select", - "options": "No\nYes", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "No\nYes" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "is_advance", "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 Advance", - "length": 0, - "no_copy": 0, "oldfieldname": "is_advance", "oldfieldtype": "Select", - "options": "No\nYes", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "No\nYes" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "fiscal_year", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Fiscal Year", - "length": 0, - "no_copy": 0, "oldfieldname": "fiscal_year", "oldfieldtype": "Select", - "options": "Fiscal Year", - "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": "Fiscal Year" }, { - "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": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Company", - "length": 0, - "no_copy": 0, "oldfieldname": "company", "oldfieldtype": "Link", "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 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": "finance_book", "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": "Finance Book", - "length": 0, - "no_copy": 0, - "options": "Finance Book", - "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": "Finance Book" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "fieldname": "to_rename", "fieldtype": "Check", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "To Rename", - "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": 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, - "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 + "label": "Due Date" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-list", "idx": 1, - "image_view": 0, "in_create": 1, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-05-01 07:05:00.366399", + "modified": "2020-02-10 04:54:57.777905", "modified_by": "Administrator", "module": "Accounts", "name": "GL Entry", "owner": "Administrator", "permissions": [ { - "amend": 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": "Accounts User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Accounts User" }, { - "amend": 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": "Accounts Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Accounts Manager" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, "report": 1, - "role": "Auditor", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Auditor" } ], "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "search_fields": "voucher_no,account,posting_date,against_voucher", - "show_name_in_global_search": 0, "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 33ee7a2974e..52a0f4e081d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1,5 +1,4 @@ { - "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-24 19:29:05", @@ -373,7 +372,8 @@ "no_copy": 1, "options": "Sales Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "column_break_21", @@ -1568,8 +1568,7 @@ "icon": "fa fa-file-text", "idx": 181, "is_submittable": 1, - "links": [], - "modified": "2019-12-30 19:15:59.580414", + "modified": "2020-02-10 04:57:11.221180", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", From 57d33f9ce6a9e7cc9148d63bd6299d3f2705de82 Mon Sep 17 00:00:00 2001 From: Govind S Menokee Date: Tue, 11 Feb 2020 11:23:16 +0530 Subject: [PATCH 105/929] fix: get_students not respecting the program The get_students function is not taking into account the program for which the fee needs to be created. If a student is enrolled for multiple programs then the fee schedule pull up the wrong count of students. --- erpnext/education/doctype/fee_schedule/fee_schedule.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.py b/erpnext/education/doctype/fee_schedule/fee_schedule.py index a42800a80d5..bba101f2f1f 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.py +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.py @@ -123,9 +123,11 @@ def get_students(student_group, academic_year, academic_term=None, student_categ students = frappe.db.sql(""" select pe.student, pe.student_name, pe.program, pe.student_batch_name - from `tabStudent Group Student` sgs, `tabProgram Enrollment` pe + from `tabStudent Group Student` sgs, `tabProgram Enrollment` pe, `tabStudent Group` sg where pe.student = sgs.student and pe.academic_year = %s + and sg.program = pe.program + and sg.name = sgs.parent and sgs.parent = %s and sgs.active = 1 {conditions} """.format(conditions=conditions), (academic_year, student_group), as_dict=1) From cde48a44cf5951228d20385461a22a1c296b82fb Mon Sep 17 00:00:00 2001 From: vishal Date: Tue, 11 Feb 2020 11:44:24 +0530 Subject: [PATCH 106/929] fix: filter serial no based on batch no --- erpnext/public/js/utils/serial_no_batch_selector.js | 5 +++-- erpnext/stock/doctype/serial_no/serial_no.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 4ec12ce8e7f..23895ee4c02 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -90,7 +90,8 @@ erpnext.SerialNoBatchSelector = Class.extend({ args: { qty: qty, item_code: me.item_code, - warehouse: me.warehouse_details.name + warehouse: me.warehouse_details.name, + batch_no: me.item.batch_no || null } }); @@ -392,7 +393,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ delivery_document_no: "" } - if (this.has_batch) { + if (this.item.batch_no) { serial_no_filters["batch_no"] = this.item.batch_no; } diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 19eb398130c..c8e9b011be9 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -397,7 +397,6 @@ def make_serial_no(serial_no, args): sr.via_stock_ledger = args.get('via_stock_ledger') or True sr.asset = args.get('asset') sr.location = args.get('location') - if args.get('purchase_document_type'): sr.purchase_document_type = args.get('purchase_document_type') @@ -474,10 +473,11 @@ def get_delivery_note_serial_no(item_code, qty, delivery_note): return serial_nos @frappe.whitelist() -def auto_fetch_serial_number(qty, item_code, warehouse): +def auto_fetch_serial_number(qty, item_code, warehouse, batch_no=None): serial_numbers = frappe.get_list("Serial No", filters={ "item_code": item_code, "warehouse": warehouse, + "batch_no": batch_no, "delivery_document_no": "", "sales_invoice": "" }, limit=qty, order_by="creation") From fc4cc9ab42ae36c5e705517e4f327b528dd471c5 Mon Sep 17 00:00:00 2001 From: Er-Naren719 <49683121+Er-Naren719@users.noreply.github.com> Date: Mon, 10 Feb 2020 13:46:47 +0530 Subject: [PATCH 107/929] feat: column width increased for view attachments field (#314) --- erpnext/projects/doctype/project_user/project_user.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/project_user/project_user.json b/erpnext/projects/doctype/project_user/project_user.json index f0a70dd1df9..2f452cc2d75 100644 --- a/erpnext/projects/doctype/project_user/project_user.json +++ b/erpnext/projects/doctype/project_user/project_user.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2016-03-25 02:52:19.283003", "doctype": "DocType", "editable_grid": 1, @@ -46,6 +47,7 @@ "fetch_from": "user.full_name", "fieldname": "full_name", "fieldtype": "Read Only", + "in_list_view": 1, "label": "Full Name" }, { @@ -55,7 +57,7 @@ "label": "Welcome email sent" }, { - "columns": 1, + "columns": 2, "default": "0", "fieldname": "view_attachments", "fieldtype": "Check", @@ -74,7 +76,8 @@ } ], "istable": 1, - "modified": "2019-07-15 19:37:26.942294", + "links": [], + "modified": "2020-02-09 23:26:50.321417", "modified_by": "Administrator", "module": "Projects", "name": "Project User", From efbfeb2d89830c32720e3fd3feb1c2707046e862 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 17 Feb 2020 10:50:05 +0530 Subject: [PATCH 108/929] fix: filters for quality_procedure tree --- .../doctype/quality_procedure/quality_procedure_tree.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js index dbdbbab3925..6df6f656aa1 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js @@ -4,7 +4,7 @@ frappe.treeview_settings["Quality Procedure"] = { add_tree_node: 'erpnext.quality_management.doctype.quality_procedure.quality_procedure.add_node', filters: [ { - fieldname: "quality_procedure", + fieldname: "parent_quality_procedure", fieldtype: "Link", options: "Quality Procedure", label: __("Quality Procedure"), From 5bfebaf1c0417141340c12f8a9f7a78139e42617 Mon Sep 17 00:00:00 2001 From: thefalconx33 Date: Mon, 17 Feb 2020 14:15:20 +0530 Subject: [PATCH 109/929] fix: disabled mode of payments fetches in sales invoices --- .../doctype/mode_of_payment/mode_of_payment.py | 13 +++++++++++++ erpnext/accounts/doctype/sales_invoice/pos.py | 7 +++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py index fcf94ce98a9..d54a47e3c96 100644 --- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py +++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py @@ -11,6 +11,7 @@ class ModeofPayment(Document): def validate(self): self.validate_accounts() self.validate_repeating_companies() + self.validate_pos_mode_of_payment() def validate_repeating_companies(self): """Error when Same Company is entered multiple times in accounts""" @@ -27,3 +28,15 @@ class ModeofPayment(Document): if frappe.db.get_value("Account", entry.default_account, "company") != entry.company: frappe.throw(_("Account {0} does not match with Company {1} in Mode of Account: {2}") .format(entry.default_account, entry.company, self.name)) + + def validate_pos_mode_of_payment(self): + if not self.enabled: + pos_profiles = frappe.db.sql("""SELECT sip.parent FROM `tabSales Invoice Payment` sip + WHERE sip.parenttype = 'POS Profile' and sip.mode_of_payment = %s""", (self.name)) + pos_profiles = list(map(lambda x: x[0], pos_profiles)) + + if pos_profiles: + message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \ + Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode." + frappe.throw(_(message), title="Not Allowed") + diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index a48d2244893..749816f7814 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -152,8 +152,11 @@ def update_multi_mode_option(doc, pos_profile): def get_mode_of_payment(doc): - return frappe.db.sql(""" select mpa.default_account, mpa.parent, mp.type as type from `tabMode of Payment Account` mpa, \ - `tabMode of Payment` mp where mpa.parent = mp.name and mpa.company = %(company)s""", {'company': doc.company}, as_dict=1) + return frappe.db.sql(""" + select mpa.default_account, mpa.parent, mp.type as type + from `tabMode of Payment Account` mpa,`tabMode of Payment` mp + where mpa.parent = mp.name and mpa.company = %(company)s and mp.enabled = 1""", + {'company': doc.company}, as_dict=1) def update_tax_table(doc): From 660b4d1e2fbef983e99bf4ad5710412b98ac0f02 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 17 Feb 2020 15:58:31 +0530 Subject: [PATCH 110/929] feat: show numeric values in item configurator (#20430) --- erpnext/portal/product_configurator/utils.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 336b9319c1d..ce507a39486 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -1,4 +1,5 @@ import frappe +import numpy as np from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager def get_field_filter_data(): @@ -172,6 +173,7 @@ def get_attributes_and_values(item_code): item_attribute_values = frappe.db.get_all('Item Attribute Value', ['parent', 'attribute_value', 'idx'], order_by='parent asc, idx asc') + item_attribute_values += get_numeric_values() ordered_attribute_value_map = frappe._dict() for iv in item_attribute_values: ordered_attribute_value_map.setdefault(iv.parent, []).append(iv.attribute_value) @@ -184,6 +186,23 @@ def get_attributes_and_values(item_code): return attributes +def get_numeric_values(): + attribute_values_list = [] + numeric_attributes = frappe.get_list("Item Attribute", fields=['name', 'from_range', 'to_range', 'increment'], filters={"numeric_values": 1}) + for attribute in numeric_attributes: + from_range = attribute["from_range"] + to_range = attribute['to_range'] + attribute['increment'] + increment = attribute['increment'] + values = list(np.arange(from_range, to_range, increment)) + + for idx, val in enumerate(values): + attribute_values_list.append(frappe._dict({ + "parent": attribute.get("name"), + "attribute_value": str(int(val)) if val.is_integer() else str(val), + "idx": idx + })) + return attribute_values_list + @frappe.whitelist(allow_guest=True) def get_next_attribute_and_values(item_code, selected_attributes): From f95ac99baa6c60a270b3a4ff52fab3b957b6c12e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 27 Nov 2019 14:53:58 +0530 Subject: [PATCH 111/929] fix(travis): add lib cups for updated frappe requirements --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 40afeee8d46..365eb67f3dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,6 +63,7 @@ install: - tar -xf /tmp/wkhtmltox.tar.xz -C /tmp - sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf - sudo chmod o+x /usr/local/bin/wkhtmltopdf + - sudo apt-get install libcups2-dev - cd ~/frappe-bench From 6f28383531a78bb452722f3e454b99c1807343b7 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 18 Feb 2020 10:51:54 +0530 Subject: [PATCH 112/929] fix: Stock Quantity not calculated on client side in Material Request Items. --- erpnext/public/js/controllers/transaction.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 512bcf9f4fb..8c022e95fb5 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -941,15 +941,19 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) { - if(doc.doctype != 'Material Request' && frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { + if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { var item = frappe.get_doc(cdt, cdn); frappe.model.round_floats_in(item, ["qty", "conversion_factor"]); item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); - item.total_weight = flt(item.stock_qty * item.weight_per_unit); refresh_field("stock_qty", item.name, item.parentfield); - refresh_field("total_weight", item.name, item.parentfield); this.toggle_conversion_factor(item); - this.calculate_net_weight(); + + if(doc.doctype != "Material Request") { + item.total_weight = flt(item.stock_qty * item.weight_per_unit); + refresh_field("total_weight", item.name, item.parentfield); + this.calculate_net_weight(); + } + if (!dont_fetch_price_list_rate && frappe.meta.has_field(doc.doctype, "price_list_currency")) { this.apply_price_list(item, true); From b63c041aa429725de0251dd8878c58a2cd2dd8d7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 18 Feb 2020 16:09:37 +0530 Subject: [PATCH 113/929] feat: Group by AR/AP report (#20574) * feat: Group by AR/AP report * fix: Do not consider total row in charts * fix: Subtotal row for last party --- .../accounts_payable/accounts_payable.js | 15 ++++ .../accounts_receivable.js | 16 +++- .../accounts_receivable.py | 75 ++++++++++++++++--- 3 files changed, 96 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index b1f427ca7f6..df700ec9d3c 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -100,6 +100,11 @@ frappe.query_reports["Accounts Payable"] = { "fieldtype": "Link", "options": "Supplier Group" }, + { + "fieldname": "group_by_party", + "label": __("Group By Supplier"), + "fieldtype": "Check" + }, { "fieldname":"based_on_payment_terms", "label": __("Based On Payment Terms"), @@ -112,6 +117,16 @@ frappe.query_reports["Accounts Payable"] = { "hidden": 1 } ], + + "formatter": function(value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + if (data && data.bold) { + value = value.bold(); + + } + return value; + }, + onload: function(report) { report.page.add_inner_button(__("Accounts Payable Summary"), function() { var filters = report.get_values(); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 9b4dda2f696..5d0154f597e 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -87,7 +87,7 @@ frappe.query_reports["Accounts Receivable"] = { frappe.query_report.set_filter_value('payment_terms', value["payment_terms"]); }); - frappe.db.get_value('Customer Credit Limit', {'parent': customer, 'company': company}, + frappe.db.get_value('Customer Credit Limit', {'parent': customer, 'company': company}, ["credit_limit"], function(value) { if (value) { frappe.query_report.set_filter_value('credit_limit', value["credit_limit"]); @@ -131,6 +131,11 @@ frappe.query_reports["Accounts Receivable"] = { "fieldtype": "Link", "options": "Sales Person" }, + { + "fieldname": "group_by_party", + "label": __("Group By Customer"), + "fieldtype": "Check" + }, { "fieldname":"based_on_payment_terms", "label": __("Based On Payment Terms"), @@ -177,6 +182,15 @@ frappe.query_reports["Accounts Receivable"] = { } ], + "formatter": function(value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + if (data && data.bold) { + value = value.bold(); + + } + return value; + }, + onload: function(report) { report.page.add_inner_button(__("Accounts Receivable Summary"), function() { var filters = report.get_values(); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index f82146a1df8..0438f6d09e1 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -46,7 +46,7 @@ class ReceivablePayableReport(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, None, self.skip_total_row def set_defaults(self): if not self.filters.get("company"): @@ -57,6 +57,12 @@ class ReceivablePayableReport(object): self.party_type = self.filters.party_type self.party_details = {} self.invoices = set() + self.skip_total_row = 0 + + if self.filters.get('group_by_party'): + self.previous_party='' + self.total_row_map = {} + self.skip_total_row = 1 def get_data(self): self.get_gl_entries() @@ -102,6 +108,12 @@ class ReceivablePayableReport(object): ) self.get_invoices(gle) + if self.filters.get('group_by_party'): + self.init_subtotal_row(gle.party) + + if self.filters.get('group_by_party'): + self.init_subtotal_row('Total') + def get_invoices(self, gle): if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'): if self.filters.get("sales_person"): @@ -111,6 +123,20 @@ class ReceivablePayableReport(object): else: self.invoices.add(gle.voucher_no) + def init_subtotal_row(self, party): + if not self.total_row_map.get(party): + self.total_row_map.setdefault(party, { + 'party': party, + 'bold': 1 + }) + + for field in self.get_currency_fields(): + self.total_row_map[party][field] = 0.0 + + def get_currency_fields(self): + return ['invoiced', 'paid', 'credit_note', 'outstanding', 'range1', + 'range2', 'range3', 'range4', 'range5'] + def update_voucher_balance(self, gle): # get the row where this balance needs to be updated # if its a payment, it will return the linked invoice or will be considered as advance @@ -135,6 +161,18 @@ class ReceivablePayableReport(object): # advance / unlinked payment or other adjustment row.paid -= gle_balance + def update_sub_total_row(self, row, party): + total_row = self.total_row_map.get(party) + + for field in self.get_currency_fields(): + total_row[field] += row.get(field, 0.0) + + def append_subtotal_row(self, party): + sub_total_row = self.total_row_map.get(party) + self.data.append(sub_total_row) + self.data.append({}) + self.update_sub_total_row(sub_total_row, 'Total') + def get_voucher_balance(self, gle): if self.filters.get("sales_person"): against_voucher = gle.against_voucher or gle.voucher_no @@ -192,11 +230,22 @@ class ReceivablePayableReport(object): else: self.append_row(row) + if self.filters.get('group_by_party'): + self.append_subtotal_row(self.previous_party) + self.data.append(self.total_row_map.get('Total')) + def append_row(self, row): self.allocate_future_payments(row) self.set_invoice_details(row) self.set_party_details(row) self.set_ageing(row) + + if self.filters.get('group_by_party'): + self.update_sub_total_row(row, row.party) + if self.previous_party and (self.previous_party != row.party): + self.append_subtotal_row(self.previous_party) + self.previous_party = row.party + self.data.append(row) def set_invoice_details(self, row): @@ -503,6 +552,7 @@ class ReceivablePayableReport(object): # get all the GL entries filtered by the given filters conditions, values = self.prepare_conditions() + order_by = self.get_order_by_condition() if self.filters.get(scrub(self.party_type)): select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit" @@ -520,9 +570,8 @@ class ReceivablePayableReport(object): and party_type=%s and (party is not null and party != '') and posting_date <= %s - {1} - order by posting_date, party""" - .format(select_fields, conditions), values, as_dict=True) + {1} {2}""" + .format(select_fields, conditions, order_by), values, as_dict=True) def get_sales_invoices_or_customers_based_on_sales_person(self): if self.filters.get("sales_person"): @@ -557,6 +606,12 @@ class ReceivablePayableReport(object): return " and ".join(conditions), values + def get_order_by_condition(self): + if self.filters.get('group_by_party'): + return "order by party, posting_date" + else: + return "order by posting_date, party" + def add_common_filters(self, conditions, values, party_type_field): if self.filters.company: conditions.append("company=%s") @@ -736,11 +791,13 @@ class ReceivablePayableReport(object): def get_chart_data(self): rows = [] for row in self.data: - values = [row.range1, row.range2, row.range3, row.range4, row.range5] - precision = cint(frappe.db.get_default("float_precision")) or 2 - rows.append({ - 'values': [flt(val, precision) for val in values] - }) + row = frappe._dict(row) + if not cint(row.bold): + values = [row.range1, row.range2, row.range3, row.range4, row.range5] + precision = cint(frappe.db.get_default("float_precision")) or 2 + rows.append({ + 'values': [flt(val, precision) for val in values] + }) self.chart = { "data": { From 1ed737cf6488389114abd833d6660b4178b1b101 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 18 Feb 2020 21:35:59 +0530 Subject: [PATCH 114/929] fix: Set Query on warehouse fields in Stock Settings --- erpnext/stock/doctype/stock_settings/stock_settings.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index 49ce3d8ef7a..755c4307187 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -3,6 +3,15 @@ frappe.ui.form.on('Stock Settings', { refresh: function(frm) { + let filters = function() { + return { + filters : { + is_group : 0 + } + }; + } + frm.set_query("default_warehouse", filters); + frm.set_query("sample_retention_warehouse", filters); } }); From 0119d15adb491d87dc8739c4cfa7a39c3e4caafe Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 19 Feb 2020 11:05:58 +0530 Subject: [PATCH 115/929] fix: Server side validation for Warehouses --- erpnext/stock/doctype/stock_settings/stock_settings.js | 6 +++--- erpnext/stock/doctype/stock_settings/stock_settings.py | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index 755c4307187..cc0e2cfc425 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -5,11 +5,11 @@ frappe.ui.form.on('Stock Settings', { refresh: function(frm) { let filters = function() { return { - filters : { + filters : { is_group : 0 - } + } }; - } + }; frm.set_query("default_warehouse", filters); frm.set_query("sample_retention_warehouse", filters); diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py index 65de2e58d3a..93b5eee75c2 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.py +++ b/erpnext/stock/doctype/stock_settings/stock_settings.py @@ -30,9 +30,17 @@ class StockSettings(Document): frappe.make_property_setter({'fieldname': name, 'property': 'hidden', 'value': 0 if self.show_barcode_field else 1}) + self.validate_warehouses() self.cant_change_valuation_method() self.validate_clean_description_html() + def validate_warehouses(self): + warehouse_fields = ["default_warehouse", "sample_retention_warehouse"] + for field in warehouse_fields: + if frappe.db.get_value("Warehouse", self.get(field), "is_group"): + frappe.throw(_("Group Warehouses cannot be used in transactions. Please change the value of {0}") \ + .format(frappe.bold(self.meta.get_field(field).label)), title =_("Incorrect Warehouse")) + def cant_change_valuation_method(self): db_valuation_method = frappe.db.get_single_value("Stock Settings", "valuation_method") From 031d4092d0e20e4567b56a2b69283bb6ea0630a5 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 19 Feb 2020 16:46:00 +0530 Subject: [PATCH 116/929] test: syncing of odometer value --- .../doctype/vehicle_log/test_vehicle_log.py | 60 ++++++++++++------- erpnext/hr/doctype/vehicle_log/vehicle_log.py | 2 +- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py index 35400b0ca83..3770da73fcf 100644 --- a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py @@ -8,25 +8,9 @@ import unittest from frappe.utils import nowdate,flt, cstr,random_string # test_records = frappe.get_test_records('Vehicle Log') class TestVehicleLog(unittest.TestCase): - def test_make_vehicle_log(self): - license_plate=random_string(10).upper() + def test_make_vehicle_log_and_syncing_of_odometer_value(self): employee_id=frappe.db.sql("""select name from `tabEmployee` order by modified desc limit 1""")[0][0] - vehicle = frappe.get_doc({ - "doctype": "Vehicle", - "license_plate": cstr(license_plate), - "make": "Maruti", - "model": "PCM", - "last_odometer":5000, - "acquisition_date":frappe.utils.nowdate(), - "location": "Mumbai", - "chassis_no": "1234ABCD", - "uom": "Litre", - "vehicle_value":frappe.utils.flt(500000) - }) - try: - vehicle.insert() - except frappe.DuplicateEntryError: - pass + license_plate = get_vehicle(employee_id) vehicle_log = frappe.get_doc({ "doctype": "Vehicle Log", "license_plate": cstr(license_plate), @@ -36,5 +20,41 @@ class TestVehicleLog(unittest.TestCase): "fuel_qty":frappe.utils.flt(50), "price": frappe.utils.flt(500) }) - vehicle_log.insert() - vehicle_log.submit() \ No newline at end of file + vehicle_log.save() + vehicle_log.submit() + + #checking value of vehicle odometer value on submit. + vehicle = frappe.get_doc("Vehicle", license_plate) + self.assertEqual(vehicle.last_odometer, vehicle_log.odometer) + + #checking value vehicle odometer on vehicle log cancellation. + last_odometer = vehicle_log.last_odometer + current_odometer = vehicle_log.odometer + distance_travelled = current_odometer - last_odometer + + vehicle_log.cancel() + vehicle.reload() + + self.assertEqual(vehicle.last_odometer, current_odometer - distance_travelled) + + +def get_vehicle(employee_id): + license_plate=random_string(10).upper() + vehicle = frappe.get_doc({ + "doctype": "Vehicle", + "license_plate": cstr(license_plate), + "make": "Maruti", + "model": "PCM", + "employee": employee_id, + "last_odometer":5000, + "acquisition_date":frappe.utils.nowdate(), + "location": "Mumbai", + "chassis_no": "1234ABCD", + "uom": "Litre", + "vehicle_value":frappe.utils.flt(500000) + }) + try: + vehicle.insert() + except frappe.DuplicateEntryError: + pass + return license_plate \ No newline at end of file diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.py b/erpnext/hr/doctype/vehicle_log/vehicle_log.py index dfdfc0d7202..12cc1dd03a6 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.py @@ -18,7 +18,7 @@ class VehicleLog(Document): if not (service_detail.service_item and service_detail.type and service_detail.frequency and service_detail.expense_amount): frappe.throw(_("Service Item,Type,frequency and expense amount are required")) - def before_save(self): + def before_insert(self): model_details = get_make_model(self.license_plate) self.make = model_details[0] self.model = model_details[1] From f6d7090f07c29c360ba9040b2ddcd83fec1fd3a5 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 19 Feb 2020 19:16:40 +0530 Subject: [PATCH 117/929] fix: earned leaves creation for zero maximum leaves (#20677) --- .../doctype/leave_application/test_leave_application.py | 9 ++++++++- erpnext/hr/utils.py | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 5dfcad6a294..4c21edb2c02 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -409,7 +409,7 @@ class TestLeaveApplication(unittest.TestCase): self.assertEqual(get_leave_balance_on(employee.name, leave_type.name, nowdate(), add_days(nowdate(), 8)), 21) - def test_earned_leave(self): + def test_earned_leaves_creation(self): leave_period = get_leave_period() employee = get_employee() leave_type = 'Test Earned Leave Type' @@ -437,6 +437,13 @@ class TestLeaveApplication(unittest.TestCase): i += 1 self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 6) + # validate earned leaves creation without maximum leaves + frappe.db.set_value('Leave Type', leave_type, 'max_leaves_allowed', 0) + while(i<14): + allocate_earned_leaves() + i += 1 + self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 14) + # test to not consider current leave in leave balance while submitting def test_current_leave_on_submit(self): employee = get_employee() diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 9b0ca4e9a1a..4f5653dc67e 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -316,7 +316,9 @@ def allocate_earned_leaves(): allocation = frappe.get_doc('Leave Allocation', allocation.name) new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves) - new_allocation = new_allocation if new_allocation <= e_leave_type.max_leaves_allowed else e_leave_type.max_leaves_allowed + + if new_allocation > e_leave_type.max_leaves_allowed and e_leave_type.max_leaves_allowed > 0: + new_allocation = e_leave_type.max_leaves_allowed if new_allocation == allocation.total_leaves_allocated: continue From cd6b60df70ac8c8712966a8599c925eb1e002450 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 20 Feb 2020 12:18:21 +0530 Subject: [PATCH 118/929] core: Added change log --- erpnext/change_log/v12/v12_5_0.md | 53 +++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 erpnext/change_log/v12/v12_5_0.md diff --git a/erpnext/change_log/v12/v12_5_0.md b/erpnext/change_log/v12/v12_5_0.md new file mode 100644 index 00000000000..e398bd7c450 --- /dev/null +++ b/erpnext/change_log/v12/v12_5_0.md @@ -0,0 +1,53 @@ +## Version 12.4.0 Release Note + +### New Features: +- **Group by Customer / Customer Group / Item / Item Group / Territory / Invoice** option in Itemised Sales Register report [#20251](https://github.com/frappe/erpnext/pull/20251) + +- **Group by Supplier / Item / Item Group / Invoice** option in Itemised Purchase Register report + +- **Group by Customer / Supplier** option in Accounts Receivable / Payable report + +- Enhanced Fixed Asset Register report [#20332](https://github.com/frappe/erpnext/pull/20332) + +- Added tax category in pos profile [#20413](https://github.com/frappe/erpnext/pull/20413) + + +### Fixes +- GL Entries were not creating on manual asset creation [#20265](https://github.com/frappe/erpnext/pull/20265) + +- Payment Entry should not be allowed against blocked Purchase Invoice until release date [#20270](https://github.com/frappe/erpnext/pull/20270) + +- Incorrect number of GL Entries error in stock entry due to precision issue [#20297](https://github.com/frappe/erpnext/pull/20297) + +- Wrong outstanding invoices fetched against employee [#20373](https://github.com/frappe/erpnext/pull/20373) + +- Finance book filtering logic in financial statements and other accounting reports [#20411](https://github.com/frappe/erpnext/pull/20410) + +- Additional discount was not appling on sales invoice created by subscription [#20432](https://github.com/frappe/erpnext/pull/20432) + +- Deprecated fetching item price based on minimum qty [#20346](https://github.com/frappe/erpnext/pull/20346) + +- Calculation of carry forwarded leaves [#20341](https://github.com/frappe/erpnext/pull/20341) + +- Quotation status will be expired based on validity only if it is not Ordered or Lost [#20365](https://github.com/frappe/erpnext/pull/20354) + +- Delete auto created batch on cancellation of Purchase Receipt / Stock Entry [#20392](https://github.com/frappe/erpnext/pull/20392) + +- Show product bundle item's availability on website based on availability of all bundled items [#20384](https://github.com/frappe/erpnext/pull/20384) + +- Show relevant suppliers on "Create Purchase Order" popup in Material Request [#20232](https://github.com/frappe/erpnext/pull/20232) + +- Cannot complete task if dependent task are not completed / cancelled [#20434](https://github.com/frappe/erpnext/pull/20434) + +- Show numeric values in item configurator [#20430](https://github.com/frappe/erpnext/pull/20430) + +- Filter serial no based on batch no [#20566](https://github.com/frappe/erpnext/pull/20566) + +- Pricing rule was not working on item groups [#20546](https://github.com/frappe/erpnext/pull/20546) + +- Disabled quick entry for doctypes with tree view [#20453](https://github.com/frappe/erpnext/pull/20453) + +### Optimisation +- Handling of large number of serial no creation via Purchase Receipt / Stock Entry (10 times faster than before!) [#20540](https://github.com/frappe/erpnext/pull/20540) + +- Update outstanding amount on Sales Invoice on submission of invoice / payment [#20557](https://github.com/frappe/erpnext/pull/20557) \ No newline at end of file From 2d2aa7d664d4c312e84a75676f5e885cca5558c0 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 20 Feb 2020 12:22:01 +0530 Subject: [PATCH 119/929] fix: check for available stock in product bundle's website warehouse (#20679) * fix: check for available stock in product bundle's website warehouse * test: earned leaves creation * fix: minor change Co-authored-by: Mangesh-Khairnar --- .../hr/doctype/leave_application/test_leave_application.py | 7 ++++--- erpnext/utilities/product.py | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 4c21edb2c02..dde913a9b62 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -439,10 +439,11 @@ class TestLeaveApplication(unittest.TestCase): # validate earned leaves creation without maximum leaves frappe.db.set_value('Leave Type', leave_type, 'max_leaves_allowed', 0) - while(i<14): + i = 0 + while(i<6): allocate_earned_leaves() i += 1 - self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 14) + self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 9) # test to not consider current leave in leave balance while submitting def test_current_leave_on_submit(self): @@ -623,4 +624,4 @@ def allocate_leaves(employee, leave_period, leave_type, new_leaves_allocated, el "docstatus": 1 }).insert() - allocate_leave.submit() \ No newline at end of file + allocate_leave.submit() diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 1c0d4c38c73..c23c1f7096d 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -129,6 +129,7 @@ def get_non_stock_item_status(item_code, item_warehouse_field): #if item belongs to product bundle, check if bundle items are in stock if frappe.db.exists("Product Bundle", item_code): items = frappe.get_doc("Product Bundle", item_code).get_all_children() - return all([ get_qty_in_stock(d.item_code, item_warehouse_field).in_stock for d in items ]) + bundle_warehouse = frappe.db.get_value('Item', item_code, item_warehouse_field) + return all([ get_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock for d in items ]) else: return 1 From 1beeb2890881740f4747ddbd3bd0a8a4c311e03a Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 20 Feb 2020 12:22:33 +0530 Subject: [PATCH 120/929] fix: check for available stock in product bundle's website warehouse (#20680) --- erpnext/utilities/product.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 1c0d4c38c73..c23c1f7096d 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -129,6 +129,7 @@ def get_non_stock_item_status(item_code, item_warehouse_field): #if item belongs to product bundle, check if bundle items are in stock if frappe.db.exists("Product Bundle", item_code): items = frappe.get_doc("Product Bundle", item_code).get_all_children() - return all([ get_qty_in_stock(d.item_code, item_warehouse_field).in_stock for d in items ]) + bundle_warehouse = frappe.db.get_value('Item', item_code, item_warehouse_field) + return all([ get_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock for d in items ]) else: return 1 From 8903258362c8023fc8dd1cffd57ae526dd2f4c5d Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 20 Feb 2020 12:30:35 +0530 Subject: [PATCH 121/929] fix: mandatory on hold comment for purchase invoice (#20668) Co-authored-by: Nabin Hait --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index d4676ff0375..737c18a8a98 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1015,7 +1015,7 @@ def unblock_invoice(name): @frappe.whitelist() -def block_invoice(name, hold_comment, release_date): +def block_invoice(name, release_date, hold_comment=None): if frappe.db.exists('Purchase Invoice', name): pi = frappe.get_doc('Purchase Invoice', name) pi.block_invoice(hold_comment, release_date) From 8e80c17602663ef78e3be16a187520fa148f866a Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 20 Feb 2020 12:32:09 +0530 Subject: [PATCH 122/929] chore: SINV set_status remove redundant function calls (#20661) --- .../accounts/doctype/sales_invoice/sales_invoice.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 0dc889c0b7a..0b793e847a7 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1237,25 +1237,28 @@ class SalesInvoice(SellingController): precision = self.precision("outstanding_amount") outstanding_amount = flt(self.outstanding_amount, precision) + due_date = getdate(self.due_date) + nowdate = getdate() + discountng_status = self.get_discounting_status() if not status: if self.docstatus == 2: status = "Cancelled" elif self.docstatus == 1: - if outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed': self.status = "Overdue and Discounted" - elif outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()): + elif outstanding_amount > 0 and due_date < nowdate: self.status = "Overdue" - elif outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed': self.status = "Unpaid and Discounted" - elif outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()): + elif outstanding_amount > 0 and due_date >= nowdate: self.status = "Unpaid" #Check if outstanding amount is 0 due to credit note issued against invoice elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): self.status = "Credit Note Issued" elif self.is_return == 1: self.status = "Return" - elif outstanding_amount <=0: + elif outstanding_amount<=0: self.status = "Paid" else: self.status = "Submitted" From ae9159fbd83e8c40411dd75505d7863a7c3627e2 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 20 Feb 2020 12:33:07 +0530 Subject: [PATCH 123/929] fix: mandatory on hold comment for purchase invoice (#20667) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index d4676ff0375..737c18a8a98 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1015,7 +1015,7 @@ def unblock_invoice(name): @frappe.whitelist() -def block_invoice(name, hold_comment, release_date): +def block_invoice(name, release_date, hold_comment=None): if frappe.db.exists('Purchase Invoice', name): pi = frappe.get_doc('Purchase Invoice', name) pi.block_invoice(hold_comment, release_date) From 4e2b9395b957bbc5d8d3c560006b9259f593d8c7 Mon Sep 17 00:00:00 2001 From: gavin Date: Thu, 20 Feb 2020 12:48:32 +0530 Subject: [PATCH 124/929] chore: drop cypress requirement (#20675) --- cypress.json | 3 - cypress/fixtures/example.json | 5 - .../opportunity/lost_reason_detail.js | 31 - cypress/plugins/index.js | 17 - cypress/support/commands.js | 25 - cypress/support/index.js | 22 - package.json | 13 +- yarn.lock | 1302 ----------------- 8 files changed, 11 insertions(+), 1407 deletions(-) delete mode 100644 cypress.json delete mode 100644 cypress/fixtures/example.json delete mode 100644 cypress/integration/opportunity/lost_reason_detail.js delete mode 100644 cypress/plugins/index.js delete mode 100644 cypress/support/commands.js delete mode 100644 cypress/support/index.js diff --git a/cypress.json b/cypress.json deleted file mode 100644 index 2be29158592..00000000000 --- a/cypress.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "baseUrl": "http://test_site_ui:8000" -} \ No newline at end of file diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json deleted file mode 100644 index da18d9352a1..00000000000 --- a/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} \ No newline at end of file diff --git a/cypress/integration/opportunity/lost_reason_detail.js b/cypress/integration/opportunity/lost_reason_detail.js deleted file mode 100644 index 9cf204889d6..00000000000 --- a/cypress/integration/opportunity/lost_reason_detail.js +++ /dev/null @@ -1,31 +0,0 @@ -context('Form', () => { - before(() => { - cy.login('Administrator', 'qwe'); - cy.visit('/desk'); - }); - - it('create a new opportunity', () => { - cy.visit('/desk#Form/Opportunity/New Opportunity 1'); - cy.get('.page-title').should('contain', 'Not Saved'); - cy.fill_field('opportunity_from', 'Customer', 'Select'); - cy.fill_field('party_name', 'Test Customer', 'Link').blur(); - cy.get('.primary-action').click(); - cy.get('.page-title').should('contain', 'Open'); - cy.get('.form-inner-toolbar button:contains("Lost")').click({ force: true }); - cy.get('.modal input[data-fieldname="lost_reason"]').as('input'); - cy.get('@input').focus().type('Higher', { delay: 200 }); - cy.get('.modal .awesomplete ul') - .should('be.visible') - .get('li:contains("Higher Price")') - .click({ force: true }); - cy.get('@input').focus().type('No Followup', { delay: 200 }); - cy.get('.modal .awesomplete ul') - .should('be.visible') - .get('li:contains("No Followup")') - .click(); - - cy.fill_field('detailed_reason', 'Test Detailed Reason', 'Text'); - cy.get('.modal button:contains("Declare Lost")').click({ force: true }); - cy.get('.page-title').should('contain', 'Lost'); - }); -}); diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js deleted file mode 100644 index cd02c3bd7a8..00000000000 --- a/cypress/plugins/index.js +++ /dev/null @@ -1,17 +0,0 @@ -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -// module.exports = (on, config) => { -// `on` is used to hook into various events Cypress emits -// `config` is the resolved Cypress config -// } diff --git a/cypress/support/commands.js b/cypress/support/commands.js deleted file mode 100644 index c1f5a772e2b..00000000000 --- a/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This is will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/cypress/support/index.js b/cypress/support/index.js deleted file mode 100644 index 872df2d5d60..00000000000 --- a/cypress/support/index.js +++ /dev/null @@ -1,22 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// import frappe commands -import '../../../frappe/cypress/support/index'; -// Import commands.js using ES2015 syntax: -import './commands'; - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/package.json b/package.json index 0e09cebf56e..9701bf3a21e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,14 @@ { - "dependencies": { - "cypress": "^3.1.4" + "name": "erpnext", + "description": "Open Source ERP System powered by the Frappe Framework", + "repository": { + "type": "git", + "url": "git+https://github.com/frappe/erpnext.git" + }, + "homepage": "https://erpnext.com", + "author": "Frappe Technologies Pvt. Ltd.", + "license": "GPL-3.0", + "bugs": { + "url": "https://github.com/frappe/erpnext/issues" } } diff --git a/yarn.lock b/yarn.lock index 6f1ff10839c..fb57ccd13af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,1305 +2,3 @@ # yarn lockfile v1 -"@cypress/listr-verbose-renderer@0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a" - integrity sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo= - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - -"@cypress/xvfb@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.3.tgz#6319afdcdcff7d1505daeeaa84484d0596189860" - integrity sha512-yYrK+/bgL3hwoRHMZG4r5fyLniCy1pXex5fimtewAY6vE/jsVs8Q37UsEO03tFlcmiLnQ3rBNMaZBYTi/+C1cw== - dependencies: - debug "^3.1.0" - lodash.once "^4.1.1" - -"@types/blob-util@1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@types/blob-util/-/blob-util-1.3.3.tgz#adba644ae34f88e1dd9a5864c66ad651caaf628a" - integrity sha512-4ahcL/QDnpjWA2Qs16ZMQif7HjGP2cw3AGjHabybjw7Vm1EKu+cfQN1D78BaZbS1WJNa1opSMF5HNMztx7lR0w== - -"@types/bluebird@3.5.18": - version "3.5.18" - resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.18.tgz#6a60435d4663e290f3709898a4f75014f279c4d6" - integrity sha512-OTPWHmsyW18BhrnG5x8F7PzeZ2nFxmHGb42bZn79P9hl+GI5cMzyPgQTwNjbem0lJhoru/8vtjAFCUOu3+gE2w== - -"@types/chai-jquery@1.1.35": - version "1.1.35" - resolved "https://registry.yarnpkg.com/@types/chai-jquery/-/chai-jquery-1.1.35.tgz#9a8f0a39ec0851b2768a8f8c764158c2a2568d04" - integrity sha512-7aIt9QMRdxuagLLI48dPz96YJdhu64p6FCa6n4qkGN5DQLHnrIjZpD9bXCvV2G0NwgZ1FAmfP214dxc5zNCfgQ== - dependencies: - "@types/chai" "*" - "@types/jquery" "*" - -"@types/chai@*": - version "4.1.7" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a" - integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA== - -"@types/chai@4.0.8": - version "4.0.8" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.8.tgz#d27600e9ba2f371e08695d90a0fe0408d89c7be7" - integrity sha512-m812CONwdZn/dMzkIJEY0yAs4apyTkTORgfB2UsMOxgkUbC205AHnm4T8I0I5gPg9MHrFc1dJ35iS75c0CJkjg== - -"@types/jquery@*": - version "3.3.29" - resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.29.tgz#680a2219ce3c9250483722fccf5570d1e2d08abd" - integrity sha512-FhJvBninYD36v3k6c+bVk1DSZwh7B5Dpb/Pyk3HKVsiohn0nhbefZZ+3JXbWQhFyt0MxSl2jRDdGQPHeOHFXrQ== - dependencies: - "@types/sizzle" "*" - -"@types/jquery@3.3.6": - version "3.3.6" - resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.6.tgz#5932ead926307ca21e5b36808257f7c926b06565" - integrity sha512-403D4wN95Mtzt2EoQHARf5oe/jEPhzBOBNrunk+ydQGW8WmkQ/E8rViRAEB1qEt/vssfGfNVD6ujP4FVeegrLg== - -"@types/lodash@4.14.87": - version "4.14.87" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.87.tgz#55f92183b048c2c64402afe472f8333f4e319a6b" - integrity sha512-AqRC+aEF4N0LuNHtcjKtvF9OTfqZI0iaBoe3dA6m/W+/YZJBZjBmW/QIZ8fBeXC6cnytSY9tBoFBqZ9uSCeVsw== - -"@types/minimatch@3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" - integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== - -"@types/mocha@2.2.44": - version "2.2.44" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.44.tgz#1d4a798e53f35212fd5ad4d04050620171cd5b5e" - integrity sha512-k2tWTQU8G4+iSMvqKi0Q9IIsWAp/n8xzdZS4Q4YVIltApoMA00wFBFdlJnmoaK1/z7B0Cy0yPe6GgXteSmdUNw== - -"@types/sinon-chai@2.7.29": - version "2.7.29" - resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-2.7.29.tgz#4db01497e2dd1908b2bd30d1782f456353f5f723" - integrity sha512-EkI/ZvJT4hglWo7Ipf9SX+J+R9htNOMjW8xiOhce7+0csqvgoF5IXqY5Ae1GqRgNtWCuaywR5HjVa1snkTqpOw== - dependencies: - "@types/chai" "*" - "@types/sinon" "*" - -"@types/sinon@*": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.0.2.tgz#234c03dd39bfa97616b28215caea3de043c63310" - integrity sha512-YvJOqPk4kh1eQyxuASDD4MDK27XWAhtw6hJ7rRayEOkkTpZkqDWpDb4OjLVzFGdapOuUgZdnqO+71Q3utCJtcA== - -"@types/sinon@7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.0.0.tgz#84e707e157ec17d3e4c2a137f41fc3f416c0551e" - integrity sha512-kcYoPw0uKioFVC/oOqafk2yizSceIQXCYnkYts9vJIwQklFRsMubTObTDrjQamUyBRd47332s85074cd/hCwxg== - -"@types/sizzle@*": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" - integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== - -ajv@^5.1.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - -ansi-escapes@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -async@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/async/-/async-2.4.0.tgz#4990200f18ea5b837c2cc4f8c031a6985c385611" - integrity sha1-SZAgDxjqW4N8LMT4wDGmmFw4VhE= - dependencies: - lodash "^4.14.0" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.6.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" - integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== - -babel-runtime@^6.18.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -bluebird@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" - integrity sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw= - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -cachedir@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-1.3.0.tgz#5e01928bf2d95b5edd94b0942188246740e0dbc4" - integrity sha512-O1ji32oyON9laVPJL1IZ5bmwd2cB46VfpxkDequezH+15FDzzVddEyrGEeX4WusDSqKxdyFdDQDEG1yo1GoWkg== - dependencies: - os-homedir "^1.0.1" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -chalk@2.4.1, chalk@^2.0.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" - integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -check-more-types@2.24.0: - version "2.24.0" - resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" - integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= - -ci-info@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" - integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== - -cli-cursor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= - dependencies: - restore-cursor "^1.0.1" - -cli-spinners@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" - integrity sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw= - -cli-truncate@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" - integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= - dependencies: - slice-ansi "0.0.4" - string-width "^1.0.1" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -combined-stream@^1.0.6, combined-stream@~1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" - integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== - dependencies: - delayed-stream "~1.0.0" - -commander@2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" - integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ== - -common-tags@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.4.0.tgz#1187be4f3d4cf0c0427d43f74eef1f73501614c0" - integrity sha1-EYe+Tz1M8MBCfUP3Tu8fc1AWFMA= - dependencies: - babel-runtime "^6.18.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -concat-stream@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" - integrity sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc= - dependencies: - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -core-js@^2.4.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.1.tgz#87416ae817de957a3f249b3b5ca475d4aaed6042" - integrity sha512-L72mmmEayPJBejKIWe2pYtGis5r0tQ5NaJekdhyXgeMQTpJoBsH0NL4ElY2LfSoV15xeQWKQ+XTTOZdyero5Xg== - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cypress@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.1.4.tgz#2af04da05e09f9d3871d05713b364472744c4216" - integrity sha512-8VJYtCAFqHXMnRDo4vdomR2CqfmhtReoplmbkXVspeKhKxU8WsZl0Nh5yeil8txxhq+YQwDrInItUqIm35Vw+g== - dependencies: - "@cypress/listr-verbose-renderer" "0.4.1" - "@cypress/xvfb" "1.2.3" - "@types/blob-util" "1.3.3" - "@types/bluebird" "3.5.18" - "@types/chai" "4.0.8" - "@types/chai-jquery" "1.1.35" - "@types/jquery" "3.3.6" - "@types/lodash" "4.14.87" - "@types/minimatch" "3.0.3" - "@types/mocha" "2.2.44" - "@types/sinon" "7.0.0" - "@types/sinon-chai" "2.7.29" - bluebird "3.5.0" - cachedir "1.3.0" - chalk "2.4.1" - check-more-types "2.24.0" - commander "2.11.0" - common-tags "1.4.0" - debug "3.1.0" - execa "0.10.0" - executable "4.1.1" - extract-zip "1.6.6" - fs-extra "4.0.1" - getos "3.1.0" - glob "7.1.2" - is-ci "1.0.10" - is-installed-globally "0.1.0" - lazy-ass "1.6.0" - listr "0.12.0" - lodash "4.17.11" - log-symbols "2.2.0" - minimist "1.2.0" - moment "2.22.2" - ramda "0.24.1" - request "2.87.0" - request-progress "0.3.1" - supports-color "5.1.0" - tmp "0.0.31" - url "0.11.0" - yauzl "2.8.0" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -date-fns@^1.27.2: - version "1.30.1" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" - integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== - -debug@2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -elegant-spinner@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" - integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -execa@0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" - integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== - dependencies: - cross-spawn "^6.0.0" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -executable@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" - integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== - dependencies: - pify "^2.2.0" - -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= - -extend@~3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extract-zip@1.6.6: - version "1.6.6" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c" - integrity sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw= - dependencies: - concat-stream "1.6.0" - debug "2.6.9" - mkdirp "0.5.0" - yauzl "2.4.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" - integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= - -fd-slicer@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" - integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= - dependencies: - pend "~1.2.0" - -figures@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.1: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -fs-extra@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.1.tgz#7fc0c6c8957f983f57f306a24e5b9ddd8d0dd880" - integrity sha1-f8DGyJV/mD9X8waiTlud3Y0N2IA= - dependencies: - graceful-fs "^4.1.2" - jsonfile "^3.0.0" - universalify "^0.1.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= - -getos@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.0.tgz#db3aa4df15a3295557ce5e81aa9e3e5cdfaa6567" - integrity sha512-i9vrxtDu5DlLVFcrbqUqGWYlZN/zZ4pGMICCAcZoYsX3JA54nYp8r5EThw5K+m2q3wszkx4Th746JstspB0H4Q== - dependencies: - async "2.4.0" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -glob@7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-dirs@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" - integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= - dependencies: - ini "^1.3.4" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6: - version "4.1.15" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" - integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" - integrity sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0= - dependencies: - ajv "^5.1.0" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" - integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" - -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.3, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== - -is-ci@1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e" - integrity sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4= - dependencies: - ci-info "^1.0.0" - -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-installed-globally@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" - integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA= - dependencies: - global-dirs "^0.1.0" - is-path-inside "^1.0.0" - -is-path-inside@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" - integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= - dependencies: - path-is-inside "^1.0.1" - -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -jsonfile@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66" - integrity sha1-pezG9l9T9mLEQVx2daAzHQmS7GY= - optionalDependencies: - graceful-fs "^4.1.6" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -lazy-ass@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" - integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= - -listr-silent-renderer@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" - integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= - -listr-update-renderer@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz#ca80e1779b4e70266807e8eed1ad6abe398550f9" - integrity sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - strip-ansi "^3.0.1" - -listr-verbose-renderer@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" - integrity sha1-ggb0z21S3cWCfl/RSYng6WWTOjU= - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - -listr@0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.12.0.tgz#6bce2c0f5603fa49580ea17cd6a00cc0e5fa451a" - integrity sha1-a84sD1YD+klYDqF81qAMwOX6RRo= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - figures "^1.7.0" - indent-string "^2.1.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.2.0" - listr-verbose-renderer "^0.4.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - ora "^0.2.3" - p-map "^1.1.1" - rxjs "^5.0.0-beta.11" - stream-to-observable "^0.1.0" - strip-ansi "^3.0.1" - -lodash.once@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= - -lodash@4.17.11, lodash@^4.14.0: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== - -log-symbols@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - -log-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" - integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= - dependencies: - chalk "^1.0.0" - -log-update@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" - integrity sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE= - dependencies: - ansi-escapes "^1.0.0" - cli-cursor "^1.0.2" - -mime-db@~1.37.0: - version "1.37.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" - integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg== - -mime-types@^2.1.12, mime-types@~2.1.17: - version "2.1.21" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" - integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg== - dependencies: - mime-db "~1.37.0" - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - -mkdirp@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" - integrity sha1-HXMHam35hs2TROFecfzAWkyavxI= - dependencies: - minimist "0.0.8" - -moment@2.22.2: - version "2.22.2" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" - integrity sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y= - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -oauth-sign@~0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" - integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM= - -object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= - -ora@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" - integrity sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q= - dependencies: - chalk "^1.1.1" - cli-cursor "^1.0.2" - cli-spinners "^0.1.2" - object-assign "^4.0.1" - -os-homedir@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-tmpdir@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-map@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" - integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-is-inside@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -pify@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -qs@~6.5.1: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -ramda@0.24.1: - version "0.24.1" - resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" - integrity sha1-w7d1UZfzW43DUCIoJixMkd22uFc= - -readable-stream@^2.2.2: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - -request-progress@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-0.3.1.tgz#0721c105d8a96ac6b2ce8b2c89ae2d5ecfcf6b3a" - integrity sha1-ByHBBdipasayzossia4tXs/Pazo= - dependencies: - throttleit "~0.0.2" - -request@2.87.0: - version "2.87.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" - integrity sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.6.0" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" - forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.17" - oauth-sign "~0.8.2" - performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - tough-cookie "~2.3.3" - tunnel-agent "^0.6.0" - uuid "^3.1.0" - -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - -rxjs@^5.0.0-beta.11: - version "5.5.12" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" - integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== - dependencies: - symbol-observable "1.0.1" - -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -semver@^5.5.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" - integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= - -sshpk@^1.7.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.0.tgz#1d4963a2fbffe58050aa9084ca20be81741c07de" - integrity sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -stream-to-observable@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe" - integrity sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4= - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - -supports-color@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.1.0.tgz#058a021d1b619f7ddf3980d712ea3590ce7de3d5" - integrity sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ== - dependencies: - has-flag "^2.0.0" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -symbol-observable@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" - integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= - -throttleit@~0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-0.0.2.tgz#cfedf88e60c00dd9697b61fdd2a8343a9b680eaf" - integrity sha1-z+34jmDADdlpe2H90qg0OptoDq8= - -tmp@0.0.31: - version "0.0.31" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" - integrity sha1-jzirlDjhcxXl29izZX6L+yd65Kc= - dependencies: - os-tmpdir "~1.0.1" - -tough-cookie@~2.3.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" - integrity sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA== - dependencies: - punycode "^1.4.1" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -url@0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -uuid@^3.1.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -yauzl@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" - integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= - dependencies: - fd-slicer "~1.0.1" - -yauzl@2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.8.0.tgz#79450aff22b2a9c5a41ef54e02db907ccfbf9ee2" - integrity sha1-eUUK/yKyqcWkHvVOAtuQfM+/nuI= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.0.1" From a6f56bbc3e7cd0e2fb470963975fdfe6bdc7190c Mon Sep 17 00:00:00 2001 From: Mathieu Brunot Date: Thu, 20 Feb 2020 08:20:09 +0100 Subject: [PATCH 125/929] chore(ci-coverage): Pin coverage 4.5.4 #20646 (#20648) * chore(ci-coverage): Pin coveralls 4.5.4 #20646 Signed-off-by: mathieu.brunot * chore: Pin coverage Signed-off-by: mathieu.brunot --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 365eb67f3dc..213445b806e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -77,5 +77,6 @@ install: - bench --site test_site reinstall --yes after_script: + - pip install coverage==4.5.4 - pip install python-coveralls - coveralls -b apps/erpnext -d ../../sites/.coverage From ab8aa43ffb18c5fdec4eaf618c203bb97e695201 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 20 Feb 2020 12:52:37 +0530 Subject: [PATCH 126/929] fix: changed field type which was affecting filters (#20670) --- erpnext/hr/doctype/attendance/attendance.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/attendance/attendance.json b/erpnext/hr/doctype/attendance/attendance.json index bc89b368d30..20974a0b7d2 100644 --- a/erpnext/hr/doctype/attendance/attendance.json +++ b/erpnext/hr/doctype/attendance/attendance.json @@ -57,11 +57,12 @@ { "fetch_from": "employee.employee_name", "fieldname": "employee_name", - "fieldtype": "Read Only", + "fieldtype": "Data", "in_global_search": 1, "label": "Employee Name", "oldfieldname": "employee_name", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "read_only": 1 }, { "default": "Present", @@ -173,7 +174,7 @@ "icon": "fa fa-ok", "idx": 1, "is_submittable": 1, - "modified": "2019-07-29 20:35:40.845422", + "modified": "2020-02-19 14:25:32.945842", "modified_by": "Administrator", "module": "HR", "name": "Attendance", From 355051bf75ced103449e67a4bdcefdd67b7f9f63 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 20 Feb 2020 12:59:21 +0530 Subject: [PATCH 127/929] fix: apply url encoding to project names (#20641) --- erpnext/templates/includes/projects/project_row.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/templates/includes/projects/project_row.html b/erpnext/templates/includes/projects/project_row.html index 55b02e20045..73c83ef560b 100644 --- a/erpnext/templates/includes/projects/project_row.html +++ b/erpnext/templates/includes/projects/project_row.html @@ -1,6 +1,6 @@ {% if doc.status=="Open" %}
- +
From c74343531fe97b995c38b5a3806304397fb3ea12 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 20 Feb 2020 13:25:45 +0530 Subject: [PATCH 128/929] fix: skip earned leaves check for max leaves set to zero or less (#20536) * fix: skip earned leaves check for max leaves set to zero or less * test: earned leaves creation --- .../leave_application/test_leave_application.py | 10 +++++++++- erpnext/hr/utils.py | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 5dfcad6a294..fc18d7a4cab 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -409,7 +409,7 @@ class TestLeaveApplication(unittest.TestCase): self.assertEqual(get_leave_balance_on(employee.name, leave_type.name, nowdate(), add_days(nowdate(), 8)), 21) - def test_earned_leave(self): + def test_earned_leaves_creation(self): leave_period = get_leave_period() employee = get_employee() leave_type = 'Test Earned Leave Type' @@ -437,6 +437,14 @@ class TestLeaveApplication(unittest.TestCase): i += 1 self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 6) + # validate earned leaves creation without maximum leaves + frappe.db.set_value('Leave Type', leave_type, 'max_leaves_allowed', 0) + i = 0 + while(i<6): + allocate_earned_leaves() + i += 1 + self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 9) + # test to not consider current leave in leave balance while submitting def test_current_leave_on_submit(self): employee = get_employee() diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 9b0ca4e9a1a..4f5653dc67e 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -316,7 +316,9 @@ def allocate_earned_leaves(): allocation = frappe.get_doc('Leave Allocation', allocation.name) new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves) - new_allocation = new_allocation if new_allocation <= e_leave_type.max_leaves_allowed else e_leave_type.max_leaves_allowed + + if new_allocation > e_leave_type.max_leaves_allowed and e_leave_type.max_leaves_allowed > 0: + new_allocation = e_leave_type.max_leaves_allowed if new_allocation == allocation.total_leaves_allocated: continue From 588a89f95719bdfcb7acf5711114f9868469a409 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 20 Feb 2020 13:41:00 +0530 Subject: [PATCH 129/929] fix: return null for attribute (#20684) --- .../doctype/amazon_mws_settings/amazon_methods.py | 6 ++++++ .../doctype/amazon_mws_settings/amazon_mws_settings.py | 5 ++--- .../doctype/amazon_mws_settings/xml_utils.py | 5 ++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py index 2f39dc596bf..3bc8db5e78a 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py @@ -165,6 +165,9 @@ def create_item_code(amazon_item_json, sku): return item.name def create_manufacturer(amazon_item_json): + if not amazon_item_json.Product.AttributeSets.ItemAttributes.Manufacturer: + return None + existing_manufacturer = frappe.db.get_value("Manufacturer", filters={"short_name":amazon_item_json.Product.AttributeSets.ItemAttributes.Manufacturer}) @@ -177,6 +180,9 @@ def create_manufacturer(amazon_item_json): return existing_manufacturer def create_brand(amazon_item_json): + if not amazon_item_json.Product.AttributeSets.ItemAttributes.Brand: + return None + existing_brand = frappe.db.get_value("Brand", filters={"brand":amazon_item_json.Product.AttributeSets.ItemAttributes.Brand}) if not existing_brand: diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py index 249a73f9fbb..c222afbb6c1 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py @@ -7,7 +7,6 @@ import frappe from frappe.model.document import Document import dateutil from frappe.custom.doctype.custom_field.custom_field import create_custom_fields -from erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_methods import get_products_details, get_orders class AmazonMWSSettings(Document): def validate(self): @@ -19,12 +18,12 @@ class AmazonMWSSettings(Document): def get_products_details(self): if self.enable_amazon == 1: - get_products_details() + frappe.enqueue('erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_methods.get_products_details') def get_order_details(self): if self.enable_amazon == 1: after_date = dateutil.parser.parse(self.after_date).strftime("%Y-%m-%d") - get_orders(after_date = after_date) + frappe.enqueue('erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_methods.get_orders', after_date=after_date) def schedule_get_order_details(): mws_settings = frappe.get_doc("Amazon MWS Settings") diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py index 58db669411a..a25a29f9e5f 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py @@ -33,7 +33,10 @@ class object_dict(dict): def __getattr__(self, item): - d = self.__getitem__(item) + try: + d = self.__getitem__(item) + except KeyError: + return None if isinstance(d, dict) and 'value' in d and len(d) == 1: return d['value'] From df15c758f6abde9577924db779e0652c303406ce Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Thu, 20 Feb 2020 15:29:09 +0530 Subject: [PATCH 130/929] fix: Additional salary can be created only for active employee --- erpnext/hr/doctype/additional_salary/additional_salary.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.js b/erpnext/hr/doctype/additional_salary/additional_salary.js index d0f64ab51bb..18f6b8b52d5 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.js +++ b/erpnext/hr/doctype/additional_salary/additional_salary.js @@ -8,7 +8,8 @@ frappe.ui.form.on('Additional Salary', { frm.set_query("employee", function() { return { filters: { - company: frm.doc.company + company: frm.doc.company, + status: "Active" } }; }); From b203406d4cb741a2cfe54587f89cd36922dc6902 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 20 Feb 2020 18:20:33 +0530 Subject: [PATCH 131/929] fix: proper release note version --- erpnext/change_log/v12/v12_5_0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/change_log/v12/v12_5_0.md b/erpnext/change_log/v12/v12_5_0.md index e398bd7c450..77499e61baf 100644 --- a/erpnext/change_log/v12/v12_5_0.md +++ b/erpnext/change_log/v12/v12_5_0.md @@ -1,4 +1,4 @@ -## Version 12.4.0 Release Note +## Version 12.5.0 Release Note ### New Features: - **Group by Customer / Customer Group / Item / Item Group / Territory / Invoice** option in Itemised Sales Register report [#20251](https://github.com/frappe/erpnext/pull/20251) @@ -50,4 +50,4 @@ ### Optimisation - Handling of large number of serial no creation via Purchase Receipt / Stock Entry (10 times faster than before!) [#20540](https://github.com/frappe/erpnext/pull/20540) -- Update outstanding amount on Sales Invoice on submission of invoice / payment [#20557](https://github.com/frappe/erpnext/pull/20557) \ No newline at end of file +- Update outstanding amount on Sales Invoice on submission of invoice / payment [#20557](https://github.com/frappe/erpnext/pull/20557) From 1b78d20d3c09b02ba470be952f1b5744cd0240ad Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Thu, 20 Feb 2020 18:46:57 +0550 Subject: [PATCH 132/929] bumped to version 12.5.0 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 12177f81da9..718552ad8ef 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.4.3' +__version__ = '12.5.0' def get_default_company(user=None): '''Get default company for user''' From 9921d28ea7357be1eb1d2791bcfdd320fb0bf08f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 24 Feb 2020 18:45:47 +0530 Subject: [PATCH 133/929] fix: account dashboard not working --- .../account_balance_timeline.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py index 43acded3a98..a9b3d7c4cc2 100644 --- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py +++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, json from frappe import _ -from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate +from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate, get_link_to_form from erpnext.accounts.report.general_ledger.general_ledger import execute from frappe.core.page.dashboard.dashboard import cache_source, get_from_date_from_timespan from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending @@ -30,8 +30,13 @@ def get(chart_name = None, chart = None, no_cache = None, from_date = None, to_d account = filters.get("account") company = filters.get("company") - if not account and chart: - frappe.throw(_("Account is not set for the dashboard chart {0}").format(chart)) + if not account and chart_name: + frappe.throw(_("Account is not set for the dashboard chart {0}") + .format(get_link_to_form("Dashboard Chart", chart_name))) + + if not frappe.db.exists("Account", account) and chart_name: + frappe.throw(_("Account {0} does not exists in the dashboard chart {1}") + .format(account, get_link_to_form("Dashboard Chart", chart_name))) if not to_date: to_date = nowdate() From a20bd89c20819a5ab6e6c31f03a3fab7efc29391 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 26 Feb 2020 11:25:30 +0530 Subject: [PATCH 134/929] fix: reorder material request not created if doctype has custom mandatory field (#20720) --- erpnext/stock/reorder_item.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py index 39fb0240236..97776739a87 100644 --- a/erpnext/stock/reorder_item.py +++ b/erpnext/stock/reorder_item.py @@ -116,6 +116,8 @@ def create_material_request(material_requests): else: exceptions_list.append(frappe.get_traceback()) + frappe.log_error(frappe.get_traceback()) + for request_type in material_requests: for company in material_requests[request_type]: try: @@ -158,6 +160,7 @@ def create_material_request(material_requests): schedule_dates = [d.schedule_date for d in mr.items] mr.schedule_date = max(schedule_dates or [nowdate()]) + mr.flags.ignore_mandatory = True mr.insert() mr.submit() mr_list.append(mr) From 0aa7aa5996c090ef0cc050570324d80a81c23819 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 26 Feb 2020 11:27:49 +0530 Subject: [PATCH 135/929] fix: rate and amount in material request copying from sales order (#20718) --- .../material_request_item/material_request_item.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index 795971b5e3d..9d1dafb136b 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-02-22 01:28:02", "doctype": "DocType", @@ -185,12 +186,14 @@ { "fieldname": "rate", "fieldtype": "Currency", - "label": "Rate" + "label": "Rate", + "no_copy": 1 }, { "fieldname": "amount", "fieldtype": "Currency", "label": "Amount", + "no_copy": 1, "read_only": 1 }, { @@ -407,7 +410,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-06-02 06:49:36.493957", + "links": [], + "modified": "2020-02-25 03:09:10.698967", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", From 2a8981929af9d9ad074ed81fb3b7762f8117c473 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 26 Feb 2020 11:36:38 +0530 Subject: [PATCH 136/929] perf: search for customer's return invoices then filter out gl_entries (#20710) --- .../payment_reconciliation.py | 18 ++++++++++++------ .../doctype/sales_invoice/sales_invoice.py | 3 +++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 2c04a27b0cd..30804961861 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -92,6 +92,7 @@ class PaymentReconciliation(Document): FROM `tab{doc}`, `tabGL Entry` WHERE (`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no) + and `tab{doc}`.{party_type_field} = %(party)s and `tab{doc}`.is_return = 1 and `tab{doc}`.return_against IS NULL and `tabGL Entry`.against_voucher_type = %(voucher_type)s and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s @@ -99,12 +100,17 @@ class PaymentReconciliation(Document): GROUP BY `tab{doc}`.name Having amount > 0 - """.format(doc=voucher_type, dr_or_cr=dr_or_cr, reconciled_dr_or_cr=reconciled_dr_or_cr), { - 'party': self.party, - 'party_type': self.party_type, - 'voucher_type': voucher_type, - 'account': self.receivable_payable_account - }, as_dict=1) + """.format( + doc=voucher_type, + dr_or_cr=dr_or_cr, + reconciled_dr_or_cr=reconciled_dr_or_cr, + party_type_field=frappe.scrub(self.party_type)), + { + 'party': self.party, + 'party_type': self.party_type, + 'voucher_type': voucher_type, + 'account': self.receivable_payable_account + }, as_dict=1) def add_payment_entries(self, entries): self.set('payments', []) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 0b793e847a7..d0fe4f4832a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1523,6 +1523,9 @@ def get_loyalty_programs(customer): else: return lp_details +def on_doctype_update(): + frappe.db.add_index("Sales Invoice", ["customer", "is_return", "return_against"]) + @frappe.whitelist() def create_invoice_discounting(source_name, target_doc=None): invoice = frappe.get_doc("Sales Invoice", source_name) From 6eaa542709d8bae385139655f5ef6f1cef0b883d Mon Sep 17 00:00:00 2001 From: RJPvT <48353029+RJPvT@users.noreply.github.com> Date: Wed, 26 Feb 2020 07:29:06 +0100 Subject: [PATCH 137/929] fix: smaller then instead of bigger then :-( dumb mistake v12 #20693 (#20701) --- erpnext/projects/doctype/task/task.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index f2ba80f096f..7cf4150298c 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -212,10 +212,10 @@ def set_multiple_status(names, status): task.save() def set_tasks_as_overdue(): - tasks = frappe.get_all("Task", filters={'status':['not in',['Cancelled', 'Closed']]}) + tasks = frappe.get_all("Task", filters={"status": ["not in", ["Cancelled", "Completed"]]}, fields=["name", "status", "review_date"]) for task in tasks: - if frappe.db.get_value("Task", task.name, "status") in 'Pending Review': - if getdate(frappe.db.get_value("Task", task.name, "review_date")) < getdate(today()): + if task.status == "Pending Review": + if getdate(task.review_date) > getdate(today()): continue frappe.get_doc("Task", task.name).update_status() From 39861c270c0dd457837755cea480cc4b08704ebc Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 26 Feb 2020 12:51:53 +0530 Subject: [PATCH 138/929] fix: same free item not working for pricing rule (#20713) --- .../doctype/pricing_rule/pricing_rule.py | 2 +- .../doctype/pricing_rule/test_pricing_rule.py | 60 +++++++++++++++++++ .../accounts/doctype/pricing_rule/utils.py | 9 +-- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 1d8d9dbcef5..fc0d8b16f71 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -252,7 +252,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa if pricing_rule.price_or_product_discount == "Price": apply_price_discount_rule(pricing_rule, item_details, args) else: - get_product_discount_rule(pricing_rule, item_details, doc) + get_product_discount_rule(pricing_rule, item_details, args, doc) item_details.has_pricing_rule = 1 diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 9c1fef69fac..2da71dfd0e0 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -326,6 +326,66 @@ class TestPricingRule(unittest.TestCase): self.assertEquals(item.discount_amount, 110) self.assertEquals(item.rate, 990) + def test_pricing_rule_for_product_discount_on_same_item(self): + frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule') + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule", + "apply_on": "Item Code", + "currency": "USD", + "items": [{ + "item_code": "_Test Item", + }], + "selling": 1, + "rate_or_discount": "Discount Percentage", + "rate": 0, + "min_qty": 0, + "max_qty": 7, + "discount_percentage": 17.5, + "price_or_product_discount": "Product", + "same_item": 1, + "free_qty": 1, + "company": "_Test Company" + } + frappe.get_doc(test_record.copy()).insert() + + # With pricing rule + so = make_sales_order(item_code="_Test Item", qty=1) + so.load_from_db() + self.assertEqual(so.items[1].is_free_item, 1) + self.assertEqual(so.items[1].item_code, "_Test Item") + + + def test_pricing_rule_for_product_discount_on_different_item(self): + frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule') + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule", + "apply_on": "Item Code", + "currency": "USD", + "items": [{ + "item_code": "_Test Item", + }], + "selling": 1, + "rate_or_discount": "Discount Percentage", + "rate": 0, + "min_qty": 0, + "max_qty": 7, + "discount_percentage": 17.5, + "price_or_product_discount": "Product", + "same_item": 0, + "free_item": "_Test Item 2", + "free_qty": 1, + "company": "_Test Company" + } + frappe.get_doc(test_record.copy()).insert() + + # With pricing rule + so = make_sales_order(item_code="_Test Item", qty=1) + so.load_from_db() + self.assertEqual(so.items[1].is_free_item, 1) + self.assertEqual(so.items[1].item_code, "_Test Item 2") + def make_pricing_rule(**args): args = frappe._dict(args) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index e475563c77c..a2bb2ee927d 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -435,7 +435,7 @@ def apply_pricing_rule_on_transaction(doc): doc.calculate_taxes_and_totals() elif d.price_or_product_discount == 'Product': item_details = frappe._dict({'parenttype': doc.doctype}) - get_product_discount_rule(d, item_details, doc) + get_product_discount_rule(d, item_details, doc=doc) apply_pricing_rule_for_free_items(doc, item_details.free_item_data) doc.set_missing_values() @@ -443,9 +443,10 @@ def get_applied_pricing_rules(item_row): return (item_row.get("pricing_rules").split(',') if item_row.get("pricing_rules") else []) -def get_product_discount_rule(pricing_rule, item_details, doc=None): - free_item = (pricing_rule.free_item - if not pricing_rule.same_item or pricing_rule.apply_on == 'Transaction' else item_details.item_code) +def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): + free_item = pricing_rule.free_item + if pricing_rule.same_item: + free_item = item_details.item_code or args.item_code if not free_item: frappe.throw(_("Free item not set in the pricing rule {0}") From 5475b5f562149699498f405c9f40fb36521e3d1e Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 26 Feb 2020 14:50:52 +0530 Subject: [PATCH 139/929] fix: validated leave allocation (#20635) * fix: validated leave allocation * fix: changes requested * Update erpnext/hr/doctype/leave_encashment/leave_encashment.py Co-Authored-By: Nabin Hait Co-authored-by: Nabin Hait --- erpnext/hr/doctype/leave_encashment/leave_encashment.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index 42f0179baf5..ad2cc02fd7c 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -64,6 +64,9 @@ class LeaveEncashment(Document): allocation = self.get_leave_allocation() + if not allocation: + frappe.throw(_("No Leaves Allocated to Employee: {0} for Leave Type: {1}").format(self.employee, self.leave_type)) + self.leave_balance = allocation.total_leaves_allocated - allocation.carry_forwarded_leaves_count\ - get_unused_leaves(self.employee, self.leave_type, allocation.from_date, self.encashment_date) @@ -116,4 +119,4 @@ def create_leave_encashment(leave_allocation): leave_type=allocation.leave_type, encashment_date=allocation.to_date )) - leave_encashment.insert(ignore_permissions=True) \ No newline at end of file + leave_encashment.insert(ignore_permissions=True) From acbc4f648d8ae40c3b6d6d53104399c8aa13cec5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 26 Feb 2020 17:17:06 +0530 Subject: [PATCH 140/929] fix: Mandatory bank account error fix (#20734) * fix: Mandatory bank account error fix * fix: SQL condition --- .../bank_reconciliation/bank_reconciliation.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 52bbe3327a8..2436b15dd4d 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -13,9 +13,11 @@ form_grid_templates = { class BankReconciliation(Document): def get_payment_entries(self): - if not (self.bank_account and self.from_date and self.to_date): - msgprint(_("Bank Account, From Date and To Date are Mandatory")) - return + if not (self.from_date and self.to_date): + frappe.throw(_("From Date and To Date are Mandatory")) + + if not self.account: + frappe.throw(_("Account is mandatory to get payment entries")) condition = "" if not self.include_reconciled_entries: @@ -37,6 +39,11 @@ class BankReconciliation(Document): order by t1.posting_date ASC, t1.name DESC """, {"condition":condition, "account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) + condition = '' + + if self.bank_account: + condition += 'and bank_account = %(bank_account)s' + payment_entries = frappe.db.sql(""" select "Payment Entry" as payment_document, name as payment_entry, @@ -49,10 +56,10 @@ class BankReconciliation(Document): where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1 and posting_date >= %(from)s and posting_date <= %(to)s - and bank_account = %(bank_account)s + {condition} order by posting_date ASC, name DESC - """, {"account": self.account, "from":self.from_date, + """.format(condition=condition), {"account": self.account, "from":self.from_date, "to": self.to_date, "bank_account": self.bank_account}, as_dict=1) pos_entries = [] From 6f35a63f2d0cd98c88a3aa16682766095356de9d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Feb 2020 15:04:43 +0530 Subject: [PATCH 141/929] fix: Mandatory bank account error fix --- .../doctype/bank_reconciliation/bank_reconciliation.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 52bbe3327a8..80880c32496 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -13,9 +13,11 @@ form_grid_templates = { class BankReconciliation(Document): def get_payment_entries(self): - if not (self.bank_account and self.from_date and self.to_date): - msgprint(_("Bank Account, From Date and To Date are Mandatory")) - return + if not (self.from_date and self.to_date): + frappe.throw(_("From Date and To Date are Mandatory")) + + if not self.account: + frappe.throw(_("Account is mandatory to get payment entries")) condition = "" if not self.include_reconciled_entries: From f3674ccc6c35d3a60831121d01022de4323ea9ee Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Feb 2020 16:09:58 +0530 Subject: [PATCH 142/929] fix: SQL condition --- .../doctype/bank_reconciliation/bank_reconciliation.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 80880c32496..2436b15dd4d 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -39,6 +39,11 @@ class BankReconciliation(Document): order by t1.posting_date ASC, t1.name DESC """, {"condition":condition, "account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) + condition = '' + + if self.bank_account: + condition += 'and bank_account = %(bank_account)s' + payment_entries = frappe.db.sql(""" select "Payment Entry" as payment_document, name as payment_entry, @@ -51,10 +56,10 @@ class BankReconciliation(Document): where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1 and posting_date >= %(from)s and posting_date <= %(to)s - and bank_account = %(bank_account)s + {condition} order by posting_date ASC, name DESC - """, {"account": self.account, "from":self.from_date, + """.format(condition=condition), {"account": self.account, "from":self.from_date, "to": self.to_date, "bank_account": self.bank_account}, as_dict=1) pos_entries = [] From 8f458feafc362dd486b751ed023ca08927897abb Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Wed, 26 Feb 2020 18:40:16 +0550 Subject: [PATCH 143/929] bumped to version 12.5.1 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 718552ad8ef..f614edbf261 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.5.0' +__version__ = '12.5.1' def get_default_company(user=None): '''Get default company for user''' From 4e3ad9c3f2768656ee3e5095d76de0e8ad8b91e1 Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 26 Feb 2020 18:51:35 +0530 Subject: [PATCH 144/929] fix: Lock stock ledger entries that are being reposted. (#20739) - If stock ledger entries are being reposted, don't let any other transaction apply itself on the same. --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2b47a2c09b8..11c61a7f714 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -420,7 +420,7 @@ def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, f for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no from `tabStock Ledger Entry` sle where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition} - order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc""".format(condition=condition), + order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition), tuple([posting_date, posting_time] + values), as_dict=True): future_stock_vouchers.append([d.voucher_type, d.voucher_no]) From 30ad21b7d8b9f91f2533b098cea4c887b80dded6 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 26 Feb 2020 18:53:48 +0530 Subject: [PATCH 145/929] fix: sort Issues chronologically (#20740) --- erpnext/support/doctype/issue/issue.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 222554bda13..51b49e8049b 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -363,8 +363,9 @@ ], "icon": "fa fa-ticket", "idx": 7, - "modified": "2019-09-11 09:03:57.465623", - "modified_by": "himanshu@erpnext.com", + "links": [], + "modified": "2020-02-26 02:19:49.477928", + "modified_by": "Administrator", "module": "Support", "name": "Issue", "owner": "Administrator", @@ -384,9 +385,9 @@ "quick_entry": 1, "search_fields": "status,customer,subject,raised_by", "sort_field": "modified", - "sort_order": "ASC", + "sort_order": "DESC", "timeline_field": "customer", "title_field": "subject", "track_changes": 1, "track_seen": 1 -} \ No newline at end of file +} From 45329232b3d4076f5acfcfa637ae0f15990c51c8 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 27 Feb 2020 12:51:23 +0530 Subject: [PATCH 146/929] fix: only update items if rate or qty changed (#20743) --- erpnext/controllers/accounts_controller.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 001da5b5ea8..5abe7366d0f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1200,6 +1200,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code")) else: child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) + if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")): + continue if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): frappe.throw(_("Cannot set quantity less than delivered quantity")) From fe337dfb66d709be2c91f8d4f24799e6f3264ba5 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 27 Feb 2020 14:27:43 +0530 Subject: [PATCH 147/929] fix: serial no material transfer performance issue (#20722) --- erpnext/stock/doctype/serial_no/serial_no.py | 84 ++++++++++++------- .../stock_ledger_entry.json | 3 +- erpnext/stock/utils.py | 2 +- 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 50f27c18e31..31f84c9aaed 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -205,6 +205,7 @@ def process_serial_no(sle): def validate_serial_no(sle, item_det): serial_nos = get_serial_nos(sle.serial_no) if sle.serial_no else [] + validate_material_transfer_entry(sle) if item_det.has_serial_no==0: if serial_nos: @@ -224,7 +225,9 @@ def validate_serial_no(sle, item_det): for serial_no in serial_nos: if frappe.db.exists("Serial No", serial_no): - sr = frappe.get_doc("Serial No", serial_no) + sr = frappe.db.get_value("Serial No", serial_no, ["name", "item_code", "batch_no", "sales_order", + "delivery_document_no", "delivery_document_type", "warehouse", + "purchase_document_no", "company"], as_dict=1) if sr.item_code!=sle.item_code: if not allow_serial_nos_with_different_item(serial_no, sle): @@ -305,6 +308,19 @@ def validate_serial_no(sle, item_det): frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}") .format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse)) +def validate_material_transfer_entry(sle_doc): + sle_doc.update({ + "skip_update_serial_no": False, + "skip_serial_no_validaiton": False + }) + + if (sle_doc.voucher_type == "Stock Entry" and sle_doc.is_cancelled == "No" and + frappe.get_cached_value("Stock Entry", sle_doc.voucher_no, "purpose") == "Material Transfer"): + if sle_doc.actual_qty < 0: + sle_doc.skip_update_serial_no = True + else: + sle_doc.skip_serial_no_validaiton = True + def validate_so_serial_no(sr, sales_order,): if not sr.sales_order or sr.sales_order!= sales_order: frappe.throw(_("""Sales Order {0} has reservation for item {1}, you can @@ -312,7 +328,8 @@ def validate_so_serial_no(sr, sales_order,): be delivered""").format(sales_order, sr.item_code, sr.name)) def has_duplicate_serial_no(sn, sle): - if sn.warehouse and sle.voucher_type != 'Stock Reconciliation': + if (sn.warehouse and not sle.skip_serial_no_validaiton + and sle.voucher_type != 'Stock Reconciliation'): return True if sn.company != sle.company: @@ -337,7 +354,7 @@ def allow_serial_nos_with_different_item(sle_serial_no, sle): """ allow_serial_nos = False if sle.voucher_type=="Stock Entry" and cint(sle.actual_qty) > 0: - stock_entry = frappe.get_doc("Stock Entry", sle.voucher_no) + stock_entry = frappe.get_cached_doc("Stock Entry", sle.voucher_no) if stock_entry.purpose in ("Repack", "Manufacture"): for d in stock_entry.get("items"): if d.serial_no and (d.s_warehouse if sle.is_cancelled=="No" else d.t_warehouse): @@ -348,6 +365,7 @@ def allow_serial_nos_with_different_item(sle_serial_no, sle): return allow_serial_nos def update_serial_nos(sle, item_det): + if sle.skip_update_serial_no: return if sle.is_cancelled == "No" and not sle.serial_no and cint(sle.actual_qty) > 0 \ and item_det.has_serial_no == 1 and item_det.serial_no_series: serial_nos = get_auto_serial_nos(item_det.serial_no_series, sle.actual_qty) @@ -367,22 +385,16 @@ def auto_make_serial_nos(args): serial_nos = get_serial_nos(args.get('serial_no')) created_numbers = [] for serial_no in serial_nos: + is_new = False if frappe.db.exists("Serial No", serial_no): - sr = frappe.get_doc("Serial No", serial_no) - sr.via_stock_ledger = True - sr.item_code = args.get('item_code') - sr.warehouse = args.get('warehouse') if args.get('actual_qty', 0) > 0 else None - sr.batch_no = args.get('batch_no') - sr.location = args.get('location') - sr.company = args.get('company') - sr.supplier = args.get('supplier') - if sr.sales_order and args.get('voucher_type') == "Stock Entry" \ - and not args.get('actual_qty', 0) > 0: - sr.sales_order = None - sr.update_serial_no_reference() - sr.save(ignore_permissions=True) + sr = frappe.get_cached_doc("Serial No", serial_no) elif args.get('actual_qty', 0) > 0: - created_numbers.append(make_serial_no(serial_no, args)) + sr = frappe.new_doc("Serial No") + is_new = True + + sr = update_args_for_serial_no(sr, serial_no, args, is_new=is_new) + if is_new: + created_numbers.append(sr.name) form_links = list(map(lambda d: frappe.utils.get_link_to_form('Serial No', d), created_numbers)) if len(form_links) == 1: @@ -399,20 +411,34 @@ def get_serial_nos(serial_no): return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n') if s.strip()] -def make_serial_no(serial_no, args): - sr = frappe.new_doc("Serial No") - sr.serial_no = serial_no - sr.item_code = args.get('item_code') - sr.company = args.get('company') - sr.batch_no = args.get('batch_no') - sr.via_stock_ledger = args.get('via_stock_ledger') or True - sr.warehouse = args.get('warehouse') +def update_args_for_serial_no(serial_no_doc, serial_no, args, is_new=False): + serial_no_doc.update({ + "item_code": args.get("item_code"), + "company": args.get("company"), + "batch_no": args.get("batch_no"), + "via_stock_ledger": args.get("via_stock_ledger") or True, + "supplier": args.get("supplier"), + "location": args.get("location"), + "warehouse": (args.get("warehouse") + if args.get("actual_qty", 0) > 0 else None) + }) - sr.validate_item() - sr.update_serial_no_reference(serial_no) - sr.db_insert() + if is_new: + serial_no_doc.serial_no = serial_no - return sr.name + if (serial_no_doc.sales_order and args.get("voucher_type") == "Stock Entry" + and not args.get("actual_qty", 0) > 0): + serial_no_doc.sales_order = None + + serial_no_doc.validate_item() + serial_no_doc.update_serial_no_reference(serial_no) + + if is_new: + serial_no_doc.db_insert() + else: + serial_no_doc.db_update() + + return serial_no_doc def update_serial_nos_after_submit(controller, parentfield): stock_ledger_entries = frappe.db.sql("""select voucher_detail_no, serial_no, actual_qty, warehouse diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json index c9eba71b0d0..c03eb79eeca 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json @@ -240,6 +240,7 @@ "options": "Company", "print_width": "150px", "read_only": 1, + "search_index": 1, "width": "150px" }, { @@ -274,7 +275,7 @@ "icon": "fa fa-list", "idx": 1, "in_create": 1, - "modified": "2019-11-27 12:17:31.522675", + "modified": "2020-02-25 22:53:33.504681", "modified_by": "Administrator", "module": "Stock", "name": "Stock Ledger Entry", diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 2c6c95393bc..f3381c76091 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -136,7 +136,7 @@ def get_bin(item_code, warehouse): bin_obj.flags.ignore_permissions = 1 bin_obj.insert() else: - bin_obj = frappe.get_doc('Bin', bin) + bin_obj = frappe.get_cached_doc('Bin', bin) bin_obj.flags.ignore_permissions = True return bin_obj From d952e71d970391d0cd7f96ec3a72623aa9ed1f7c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 27 Feb 2020 16:01:47 +0530 Subject: [PATCH 148/929] fix: Journal Entry not being fetched in Bank Reconciliation --- .../doctype/bank_reconciliation/bank_reconciliation.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 2436b15dd4d..883c4207ea9 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -21,7 +21,7 @@ class BankReconciliation(Document): condition = "" if not self.include_reconciled_entries: - condition = " and (clearance_date is null or clearance_date='0000-00-00')" + condition = "and clearance_date IS NULL or clearance_date='0000-00-00'" journal_entries = frappe.db.sql(""" select @@ -34,11 +34,10 @@ class BankReconciliation(Document): where t2.parent = t1.name and t2.account = %(account)s and t1.docstatus=1 and t1.posting_date >= %(from)s and t1.posting_date <= %(to)s - and ifnull(t1.is_opening, 'No') = 'No' %(condition)s + and ifnull(t1.is_opening, 'No') = 'No' {condition} group by t2.account, t1.name order by t1.posting_date ASC, t1.name DESC - """, {"condition":condition, "account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) - + """.format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1, debug=1) condition = '' if self.bank_account: From 5fb66a395302906151a6d6362094bf253d18ad1a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 27 Feb 2020 16:04:01 +0530 Subject: [PATCH 149/929] fix: Remove debug statement --- .../doctype/bank_reconciliation/bank_reconciliation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 883c4207ea9..8900767324c 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -21,7 +21,7 @@ class BankReconciliation(Document): condition = "" if not self.include_reconciled_entries: - condition = "and clearance_date IS NULL or clearance_date='0000-00-00'" + condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')" journal_entries = frappe.db.sql(""" select @@ -37,7 +37,7 @@ class BankReconciliation(Document): and ifnull(t1.is_opening, 'No') = 'No' {condition} group by t2.account, t1.name order by t1.posting_date ASC, t1.name DESC - """.format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1, debug=1) + """.format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) condition = '' if self.bank_account: From f08b5e486627e44bd230c6b484210a609287e167 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 27 Feb 2020 19:07:39 +0530 Subject: [PATCH 150/929] feat: ignore permission when deleting linked emails (#20753) --- erpnext/setup/doctype/company/delete_company_transactions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py index 3052191943c..9979bc51e3a 100644 --- a/erpnext/setup/doctype/company/delete_company_transactions.py +++ b/erpnext/setup/doctype/company/delete_company_transactions.py @@ -106,11 +106,11 @@ def delete_lead_addresses(company_name): frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads))) def delete_communications(doctype, company_name, company_fieldname): - + refrence_docs = frappe.get_all(doctype, filters={company_fieldname:company_name}) reference_doctype_names = [r.name for r in refrence_docs] communications = frappe.get_all("Communication", filters={"reference_doctype":doctype,"reference_name":["in",reference_doctype_names]}) communication_names = [c.name for c in communications] - frappe.delete_doc("Communication",communication_names) + frappe.delete_doc("Communication", communication_names, ignore_permissions=True) From d17e0b5acab4812fc800b243de0cec09314ac823 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 28 Feb 2020 12:29:11 +0530 Subject: [PATCH 151/929] fix: Item Wise report query fix (#20761) --- .../item_wise_purchase_register.py | 8 ++------ .../item_wise_sales_register.py | 10 +++------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 8b6359c134d..4523f66deb4 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -306,10 +306,6 @@ def get_conditions(filters): def get_items(filters, additional_query_columns): conditions = get_conditions(filters) - match_conditions = frappe.build_match_conditions("Purchase Invoice") - - if match_conditions: - match_conditions = " and {0} ".format(match_conditions) if additional_query_columns: additional_query_columns = ', ' + ', '.join(additional_query_columns) @@ -327,8 +323,8 @@ def get_items(filters, additional_query_columns): `tabPurchase Invoice`.supplier_name, `tabPurchase Invoice`.mode_of_payment {0} from `tabPurchase Invoice`, `tabPurchase Invoice Item` where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and - `tabPurchase Invoice`.docstatus = 1 %s %s - """.format(additional_query_columns) % (conditions, match_conditions), filters, as_dict=1) + `tabPurchase Invoice`.docstatus = 1 %s + """.format(additional_query_columns) % (conditions), filters, as_dict=1) def get_aii_accounts(): return dict(frappe.db.sql("select name, stock_received_but_not_billed from tabCompany")) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 2cc2db6ca59..786e04dd5aa 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -119,7 +119,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns) data.append(total_row_map.get('total_row')) skip_total_row = 1 - + return columns, data, None, None, None, skip_total_row def get_columns(additional_table_columns, filters): @@ -370,10 +370,6 @@ def get_group_by_conditions(filters, doctype): def get_items(filters, additional_query_columns): conditions = get_conditions(filters) - match_conditions = frappe.build_match_conditions("Sales Invoice") - - if match_conditions: - match_conditions = " and {0} ".format(match_conditions) if additional_query_columns: additional_query_columns = ', ' + ', '.join(additional_query_columns) @@ -394,8 +390,8 @@ def get_items(filters, additional_query_columns): `tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0} from `tabSales Invoice`, `tabSales Invoice Item` where `tabSales Invoice`.name = `tabSales Invoice Item`.parent - and `tabSales Invoice`.docstatus = 1 {1} {2} - """.format(additional_query_columns or '', conditions, match_conditions), filters, as_dict=1) #nosec + and `tabSales Invoice`.docstatus = 1 {1} + """.format(additional_query_columns or '', conditions), filters, as_dict=1) #nosec def get_delivery_notes_against_sales_order(item_list): so_dn_map = frappe._dict() From b7790c0394c5dce5ee63010c9fede18e247540c7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 28 Feb 2020 12:30:29 +0530 Subject: [PATCH 152/929] fix: Item Wise report query fix (#20765) --- .../item_wise_purchase_register.py | 8 ++------ .../item_wise_sales_register.py | 10 +++------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 8b6359c134d..4523f66deb4 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -306,10 +306,6 @@ def get_conditions(filters): def get_items(filters, additional_query_columns): conditions = get_conditions(filters) - match_conditions = frappe.build_match_conditions("Purchase Invoice") - - if match_conditions: - match_conditions = " and {0} ".format(match_conditions) if additional_query_columns: additional_query_columns = ', ' + ', '.join(additional_query_columns) @@ -327,8 +323,8 @@ def get_items(filters, additional_query_columns): `tabPurchase Invoice`.supplier_name, `tabPurchase Invoice`.mode_of_payment {0} from `tabPurchase Invoice`, `tabPurchase Invoice Item` where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and - `tabPurchase Invoice`.docstatus = 1 %s %s - """.format(additional_query_columns) % (conditions, match_conditions), filters, as_dict=1) + `tabPurchase Invoice`.docstatus = 1 %s + """.format(additional_query_columns) % (conditions), filters, as_dict=1) def get_aii_accounts(): return dict(frappe.db.sql("select name, stock_received_but_not_billed from tabCompany")) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 2cc2db6ca59..786e04dd5aa 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -119,7 +119,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns) data.append(total_row_map.get('total_row')) skip_total_row = 1 - + return columns, data, None, None, None, skip_total_row def get_columns(additional_table_columns, filters): @@ -370,10 +370,6 @@ def get_group_by_conditions(filters, doctype): def get_items(filters, additional_query_columns): conditions = get_conditions(filters) - match_conditions = frappe.build_match_conditions("Sales Invoice") - - if match_conditions: - match_conditions = " and {0} ".format(match_conditions) if additional_query_columns: additional_query_columns = ', ' + ', '.join(additional_query_columns) @@ -394,8 +390,8 @@ def get_items(filters, additional_query_columns): `tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0} from `tabSales Invoice`, `tabSales Invoice Item` where `tabSales Invoice`.name = `tabSales Invoice Item`.parent - and `tabSales Invoice`.docstatus = 1 {1} {2} - """.format(additional_query_columns or '', conditions, match_conditions), filters, as_dict=1) #nosec + and `tabSales Invoice`.docstatus = 1 {1} + """.format(additional_query_columns or '', conditions), filters, as_dict=1) #nosec def get_delivery_notes_against_sales_order(item_list): so_dn_map = frappe._dict() From d2f26e57d2a9306ecd78c8a838d34d1eb81230bb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 27 Feb 2020 16:01:47 +0530 Subject: [PATCH 153/929] fix: Journal Entry not being fetched in Bank Reconciliation --- .../doctype/bank_reconciliation/bank_reconciliation.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 2436b15dd4d..883c4207ea9 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -21,7 +21,7 @@ class BankReconciliation(Document): condition = "" if not self.include_reconciled_entries: - condition = " and (clearance_date is null or clearance_date='0000-00-00')" + condition = "and clearance_date IS NULL or clearance_date='0000-00-00'" journal_entries = frappe.db.sql(""" select @@ -34,11 +34,10 @@ class BankReconciliation(Document): where t2.parent = t1.name and t2.account = %(account)s and t1.docstatus=1 and t1.posting_date >= %(from)s and t1.posting_date <= %(to)s - and ifnull(t1.is_opening, 'No') = 'No' %(condition)s + and ifnull(t1.is_opening, 'No') = 'No' {condition} group by t2.account, t1.name order by t1.posting_date ASC, t1.name DESC - """, {"condition":condition, "account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) - + """.format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1, debug=1) condition = '' if self.bank_account: From a51c56c4b3ad70d8c1fc43cb2f1125da5856a2b1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 27 Feb 2020 16:04:01 +0530 Subject: [PATCH 154/929] fix: Remove debug statement --- .../doctype/bank_reconciliation/bank_reconciliation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 883c4207ea9..8900767324c 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -21,7 +21,7 @@ class BankReconciliation(Document): condition = "" if not self.include_reconciled_entries: - condition = "and clearance_date IS NULL or clearance_date='0000-00-00'" + condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')" journal_entries = frappe.db.sql(""" select @@ -37,7 +37,7 @@ class BankReconciliation(Document): and ifnull(t1.is_opening, 'No') = 'No' {condition} group by t2.account, t1.name order by t1.posting_date ASC, t1.name DESC - """.format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1, debug=1) + """.format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) condition = '' if self.bank_account: From 3fcd575f53c71f9467da43e798037a9b4ce09e8a Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 28 Feb 2020 12:58:41 +0530 Subject: [PATCH 155/929] chore: Rearranged Buying Module Dashboard --- erpnext/config/buying.py | 54 ++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index 6f5ab32b631..1d4054786e6 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -7,6 +7,13 @@ def get_data(): "label": _("Purchasing"), "icon": "fa fa-star", "items": [ + { + "type": "doctype", + "name": "Material Request", + "onboard": 1, + "dependencies": ["Item"], + "description": _("Request for purchase."), + }, { "type": "doctype", "name": "Purchase Order", @@ -20,13 +27,6 @@ def get_data(): "onboard": 1, "dependencies": ["Item", "Supplier"] }, - { - "type": "doctype", - "name": "Material Request", - "onboard": 1, - "dependencies": ["Item"], - "description": _("Request for purchase."), - }, { "type": "doctype", "name": "Request for Quotation", @@ -63,6 +63,11 @@ def get_data(): "name": "Price List", "description": _("Price List master.") }, + { + "type": "doctype", + "name": "Pricing Rule", + "description": _("Rules for applying pricing and discount.") + }, { "type": "doctype", "name": "Product Bundle", @@ -80,11 +85,6 @@ def get_data(): "type": "doctype", "name": "Promotional Scheme", "description": _("Rules for applying different promotional schemes.") - }, - { - "type": "doctype", - "name": "Pricing Rule", - "description": _("Rules for applying pricing and discount.") } ] }, @@ -149,13 +149,6 @@ def get_data(): "reference_doctype": "Purchase Order", "onboard": 1 }, - { - "type": "report", - "is_query_report": True, - "name": "Supplier-Wise Sales Analytics", - "reference_doctype": "Stock Ledger Entry", - "onboard": 1 - }, { "type": "report", "is_query_report": True, @@ -177,6 +170,16 @@ def get_data(): "reference_doctype": "Material Request", "onboard": 1, }, + { + "type": "report", + "is_query_report": True, + "name": "Address And Contacts", + "label": _("Supplier Addresses And Contacts"), + "reference_doctype": "Address", + "route_options": { + "party_type": "Supplier" + } + } ] }, { @@ -226,18 +229,15 @@ def get_data(): { "type": "report", "is_query_report": True, - "name": "Material Requests for which Supplier Quotations are not created", - "reference_doctype": "Material Request" + "name": "Supplier-Wise Sales Analytics", + "reference_doctype": "Stock Ledger Entry", + "onboard": 1 }, { "type": "report", "is_query_report": True, - "name": "Address And Contacts", - "label": _("Supplier Addresses And Contacts"), - "reference_doctype": "Address", - "route_options": { - "party_type": "Supplier" - } + "name": "Material Requests for which Supplier Quotations are not created", + "reference_doctype": "Material Request" } ] }, From 2fc58b33277cfdb1b8067e3edc5a2add7f5669a2 Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Fri, 28 Feb 2020 15:26:03 +0550 Subject: [PATCH 156/929] bumped to version 12.5.2 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index f614edbf261..23baa838d93 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.5.1' +__version__ = '12.5.2' def get_default_company(user=None): '''Get default company for user''' From 1041516e8ef43640c5ff48c80f92f346c97a6519 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 2 Mar 2020 15:00:43 +0530 Subject: [PATCH 157/929] fix: reconciled entries showing in bank reco (#20788) --- .../accounts/doctype/bank_reconciliation/bank_reconciliation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 8900767324c..48fd154a4db 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -38,7 +38,6 @@ class BankReconciliation(Document): group by t2.account, t1.name order by t1.posting_date ASC, t1.name DESC """.format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) - condition = '' if self.bank_account: condition += 'and bank_account = %(bank_account)s' From 27163e60bbabea83ad85f62bc55af7c015d1eec7 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 2 Mar 2020 15:03:18 +0530 Subject: [PATCH 158/929] feat: link serial no to batch no (#20779) * feat: link serial no to batch no * fix: test cases --- erpnext/controllers/stock_controller.py | 4 ++++ .../purchase_receipt/test_purchase_receipt.py | 23 +++++++++++++++++++ .../stock/doctype/serial_no/serial_no.json | 6 +++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 11c61a7f714..2ab1575ee21 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -239,6 +239,10 @@ class StockController(AccountsController): for d in self.items: if not d.batch_no: continue + serial_nos = [d.name for d in frappe.get_all("Serial No", {'batch_no': d.batch_no})] + if serial_nos: + frappe.db.set_value("Serial No", { 'name': ['in', serial_nos] }, "batch_no", None) + d.batch_no = None d.db_set("batch_no", None) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index aa09d3bfe5f..7ea84e99131 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -7,6 +7,7 @@ import frappe, erpnext import frappe.defaults from frappe.utils import cint, flt, cstr, today, random_string from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice +from erpnext.stock.doctype.item.test_item import create_item from erpnext import set_perpetual_inventory from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError from erpnext.accounts.doctype.account.test_account import get_inventory_account @@ -50,6 +51,28 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(current_bin_stock_value, existing_bin_stock_value + 250) self.assertFalse(get_gl_entries("Purchase Receipt", pr.name)) + + def test_batched_serial_no_purchase(self): + item = frappe.get_doc("Item", { 'item_name': 'Batched Serialized Item' }) + if not item: + item = create_item("Batched Serialized Item") + item.has_batch_no = 1 + item.create_new_batch = 1 + item.has_serial_no = 1 + item.batch_number_series = "BS-BATCH-.##" + item.serial_no_series = "BS-.####" + item.save() + + pr = make_purchase_receipt(item_code=item.name, qty=5, rate=500) + + self.assertTrue(frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name})) + + pr.load_from_db() + batch_no = pr.items[0].batch_no + pr.cancel() + + self.assertFalse(frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name})) + self.assertFalse(frappe.db.get_all('Serial No', {'batch_no': batch_no})) def test_purchase_receipt_gl_entry(self): pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", get_multiple_items = True, get_taxes_and_charges = True) diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index 712aadc5254..fb28b5c079d 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.json +++ b/erpnext/stock/doctype/serial_no/serial_no.json @@ -6,6 +6,7 @@ "description": "Distinct unit of an Item", "doctype": "DocType", "document_type": "Setup", + "engine": "InnoDB", "field_order": [ "details", "column_break0", @@ -104,10 +105,11 @@ }, { "fieldname": "batch_no", - "fieldtype": "Data", + "fieldtype": "Link", "in_list_view": 1, "in_standard_filter": 1, "label": "Batch No", + "options": "Batch", "read_only": 1 }, { @@ -425,7 +427,7 @@ ], "icon": "fa fa-barcode", "idx": 1, - "modified": "2019-08-07 17:28:32.243280", + "modified": "2020-02-28 19:31:09.357323", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", From a6bf96322bb1d6d51e45d895682472c03ab2b4aa Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 2 Mar 2020 15:19:28 +0530 Subject: [PATCH 159/929] chore: control reposting of future gl entries with flags (#20775) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 2 +- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- erpnext/controllers/stock_controller.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 737c18a8a98..518c4d36af4 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -368,7 +368,7 @@ class PurchaseInvoice(BuyingController): update_outstanding_amt(self.credit_to, "Supplier", self.supplier, self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name) - if repost_future_gle and cint(self.update_stock) and self.auto_accounting_for_stock: + if (repost_future_gle or self.flags.repost_future_gle) and cint(self.update_stock) and self.auto_accounting_for_stock: from erpnext.controllers.stock_controller import update_gl_entries_after items, warehouses = self.get_items_and_warehouses() update_gl_entries_after(self.posting_date, self.posting_time, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index d0fe4f4832a..8258833254b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -717,7 +717,7 @@ class SalesInvoice(SellingController): update_outstanding_amt(self.debit_to, "Customer", self.customer, self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name) - if repost_future_gle and cint(self.update_stock) \ + if (repost_future_gle or self.flags.repost_future_gle) and cint(self.update_stock) \ and cint(auto_accounting_for_stock): items, warehouses = self.get_items_and_warehouses() update_gl_entries_after(self.posting_date, self.posting_time, diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2ab1575ee21..d13edc34573 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -34,7 +34,7 @@ class StockController(AccountsController): gl_entries = self.get_gl_entries(warehouse_account) make_gl_entries(gl_entries, from_repost=from_repost) - if repost_future_gle: + if (repost_future_gle or self.flags.repost_future_gle): items, warehouses = self.get_items_and_warehouses() update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items, warehouse_account, company=self.company) From 94200b1179dd6d77f2714b697b7749f0b8e325ea Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 2 Mar 2020 15:51:22 +0530 Subject: [PATCH 160/929] chore: Item Price and Product Bundle Form cleanup (#20773) * chore: Item Price and Product Bundle Form cleanup * fix: Trailing comma --- .../accounts/doctype/pricing_rule/utils.py | 6 +- .../product_bundle_item.json | 247 +++++------------- .../stock/doctype/item_price/item_price.json | 11 +- .../doctype/item_price/test_records.json | 13 +- 4 files changed, 75 insertions(+), 202 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index a2bb2ee927d..2ffa27f6d99 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -245,7 +245,7 @@ def filter_pricing_rules(args, pricing_rules, doc=None): def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, transaction_type): fieldname, msg = '', '' - type_of_transaction = 'purcahse' if transaction_type == "buying" else "sale" + type_of_transaction = 'purchase' if transaction_type == 'buying' else 'sale' for field, value in {'min_qty': qty, 'min_amt': amount}.items(): if (args.get(field) and value < args.get(field) @@ -465,7 +465,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): item_details.free_item_data.update(item_data) item_details.free_item_data['uom'] = pricing_rule.free_item_uom or item_data.stock_uom - item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item, + item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item, item_details.free_item_data['uom']).get("conversion_factor", 1) if item_details.get("parenttype") == 'Purchase Order': @@ -508,7 +508,7 @@ def validate_coupon_code(coupon_name): frappe.throw(_("Sorry,coupon code validity has not started")) elif coupon.valid_upto: if coupon.valid_upto < getdate(today()) : - frappe.throw(_("Sorry,coupon code validity has expired")) + frappe.throw(_("Sorry,coupon code validity has expired")) elif coupon.used>=coupon.maximum_use: frappe.throw(_("Sorry,coupon code are exhausted")) else: diff --git a/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json index 38f51dead49..dc071e4d65e 100644 --- a/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json +++ b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json @@ -1,189 +1,76 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-05-23 16:55:51", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2013-05-23 16:55:51", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "qty", + "description", + "rate", + "uom" + ], "fields": [ { - "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": 0, - "label": "Item", - "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": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "label": "Item", + "oldfieldname": "item_code", + "oldfieldtype": "Link", + "options": "Item", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Qty", - "length": 0, - "no_copy": 0, - "oldfieldname": "qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Qty", + "oldfieldname": "qty", + "oldfieldtype": "Currency", + "reqd": 1 + }, { - "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": 1, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "300px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "description", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "print_width": "300px" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rate", - "fieldtype": "Float", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rate", - "length": 0, - "no_copy": 0, - "oldfieldname": "rate", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "rate", + "fieldtype": "Float", + "hidden": 1, + "label": "Rate", + "oldfieldname": "rate", + "oldfieldtype": "Currency", + "print_hide": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "UOM", - "length": 0, - "no_copy": 0, - "oldfieldname": "uom", - "oldfieldtype": "Link", - "options": "UOM", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "uom", + "fieldtype": "Link", + "in_list_view": 1, + "label": "UOM", + "oldfieldname": "uom", + "oldfieldtype": "Link", + "options": "UOM", + "read_only": 1 } - ], - "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": "2017-02-20 13:24:05.633546", - "modified_by": "Administrator", - "module": "Selling", - "name": "Product Bundle Item", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "track_changes": 1, - "track_seen": 0 + ], + "idx": 1, + "istable": 1, + "links": [], + "modified": "2020-02-28 14:06:05.725655", + "modified_by": "Administrator", + "module": "Selling", + "name": "Product Bundle Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/stock/doctype/item_price/item_price.json b/erpnext/stock/doctype/item_price/item_price.json index 8456b584ca0..2e0ddfdaefc 100644 --- a/erpnext/stock/doctype/item_price/item_price.json +++ b/erpnext/stock/doctype/item_price/item_price.json @@ -10,7 +10,6 @@ "item_code", "uom", "packing_unit", - "min_qty", "column_break_17", "item_name", "brand", @@ -63,13 +62,6 @@ "fieldtype": "Int", "label": "Packing Unit" }, - { - "default": "1", - "fieldname": "min_qty", - "fieldtype": "Int", - "in_list_view": 1, - "label": "Minimum Qty " - }, { "fieldname": "column_break_17", "fieldtype": "Column Break" @@ -216,7 +208,7 @@ "icon": "fa fa-flag", "idx": 1, "links": [], - "modified": "2019-12-31 03:11:09.702250", + "modified": "2020-02-28 14:21:25.580331", "modified_by": "Administrator", "module": "Stock", "name": "Item Price", @@ -251,6 +243,7 @@ } ], "quick_entry": 1, + "sort_field": "modified", "sort_order": "ASC", "title_field": "item_name", "track_changes": 1 diff --git a/erpnext/stock/doctype/item_price/test_records.json b/erpnext/stock/doctype/item_price/test_records.json index 473bacb3c33..0a3d7e81985 100644 --- a/erpnext/stock/doctype/item_price/test_records.json +++ b/erpnext/stock/doctype/item_price/test_records.json @@ -4,7 +4,6 @@ "item_code": "_Test Item", "price_list": "_Test Price List", "price_list_rate": 100, - "min_qty": 2, "valid_from": "2017-04-18", "valid_upto": "2017-04-26" }, @@ -12,8 +11,7 @@ "doctype": "Item Price", "item_code": "_Test Item", "price_list": "_Test Price List Rest of the World", - "price_list_rate": 10, - "min_qty": 5 + "price_list_rate": 10 }, { "doctype": "Item Price", @@ -22,7 +20,6 @@ "price_list_rate": 20, "valid_from": "2017-04-18", "valid_upto": "2017-04-26", - "min_qty": 7, "customer": "_Test Customer", "uom": "_Test UOM" }, @@ -31,19 +28,15 @@ "item_code": "_Test Item Home Desktop 100", "price_list": "_Test Price List", "price_list_rate": 1000, - "min_qty" : 10, "valid_from": "2017-04-10", - "valid_upto": "2017-04-17", - "min_qty": 2 + "valid_upto": "2017-04-17" }, { "doctype": "Item Price", "item_code": "_Test Item Home Desktop Manufactured", "price_list": "_Test Price List", "price_list_rate": 1000, - "min_qty" : 10, "valid_from": "2017-04-10", - "valid_upto": "2017-04-17", - "min_qty": 2 + "valid_upto": "2017-04-17" } ] From acd93b3bd142364661b2065af8cde00c53136a47 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 2 Mar 2020 18:05:59 +0530 Subject: [PATCH 161/929] fix: use system language to translate strings (#20792) --- .../connectors/woocommerce_connection.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 28c2ab9e545..4422d23e380 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -47,11 +47,12 @@ def _order(*args, **kwargs): return "success" if event == "created": + sys_lang = frappe.get_single("System Settings").language or 'en' raw_billing_data = order.get("billing") customer_name = raw_billing_data.get("first_name") + " " + raw_billing_data.get("last_name") link_customer_and_address(raw_billing_data, customer_name) - link_items(order.get("line_items"), woocommerce_settings) - create_sales_order(order, woocommerce_settings, customer_name) + link_items(order.get("line_items"), woocommerce_settings, sys_lang) + create_sales_order(order, woocommerce_settings, customer_name, sys_lang) def link_customer_and_address(raw_billing_data, customer_name): customer_woo_com_email = raw_billing_data.get("email") @@ -100,7 +101,7 @@ def link_customer_and_address(raw_billing_data, customer_name): frappe.rename_doc("Address", old_address_title, new_address_title) -def link_items(items_list, woocommerce_settings): +def link_items(items_list, woocommerce_settings, sys_lang): for item_data in items_list: item_woo_com_id = item_data.get("product_id") @@ -112,14 +113,14 @@ def link_items(items_list, woocommerce_settings): item = frappe.new_doc("Item") item.item_name = item_data.get("name") - item.item_code = _("woocommerce - {0}").format(item_data.get("product_id")) + item.item_code = _("woocommerce - {0}", sys_lang).format(item_data.get("product_id")) item.woocommerce_id = item_data.get("product_id") - item.item_group = _("WooCommerce Products") - item.stock_uom = woocommerce_settings.uom or _("Nos") + item.item_group = _("WooCommerce Products", sys_lang) + item.stock_uom = woocommerce_settings.uom or _("Nos", sys_lang) item.flags.ignore_mandatory = True item.save() -def create_sales_order(order, woocommerce_settings, customer_name): +def create_sales_order(order, woocommerce_settings, customer_name, sys_lang): new_sales_order = frappe.new_doc("Sales Order") new_sales_order.customer = customer_name @@ -133,14 +134,14 @@ def create_sales_order(order, woocommerce_settings, customer_name): new_sales_order.company = woocommerce_settings.company - set_items_in_sales_order(new_sales_order, woocommerce_settings, order) + set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_lang) new_sales_order.flags.ignore_mandatory = True new_sales_order.insert() new_sales_order.submit() frappe.db.commit() -def set_items_in_sales_order(new_sales_order, woocommerce_settings, order): +def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_lang): company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr') for item in order.get("line_items"): @@ -154,10 +155,10 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order): "item_name": found_item.item_name, "description": found_item.item_name, "delivery_date": new_sales_order.delivery_date, - "uom": woocommerce_settings.uom or _("Nos"), + "uom": woocommerce_settings.uom or _("Nos", sys_lang), "qty": item.get("quantity"), "rate": item.get("price"), - "warehouse": woocommerce_settings.warehouse or _("Stores - {0}").format(company_abbr) + "warehouse": woocommerce_settings.warehouse or _("Stores - {0}", sys_lang).format(company_abbr) }) add_tax_details(new_sales_order, ordered_items_tax, "Ordered Item tax", woocommerce_settings.tax_account) From 6a734ecd34e45a3c096d5e590ad1d33c441ba400 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Mon, 2 Mar 2020 18:06:21 +0530 Subject: [PATCH 162/929] fix: use get all instead of get list to fetch item attributes (#20793) --- erpnext/portal/product_configurator/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index ce507a39486..327a128068f 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -188,7 +188,9 @@ def get_attributes_and_values(item_code): def get_numeric_values(): attribute_values_list = [] - numeric_attributes = frappe.get_list("Item Attribute", fields=['name', 'from_range', 'to_range', 'increment'], filters={"numeric_values": 1}) + numeric_attributes = frappe.db.get_all("Item Attribute", + fields=['name', 'from_range', 'to_range', 'increment'], + filters={"numeric_values": 1}) for attribute in numeric_attributes: from_range = attribute["from_range"] to_range = attribute['to_range'] + attribute['increment'] From d7839eb8acde1f0238aa8db74adda442eaaa369f Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 2 Mar 2020 18:09:24 +0530 Subject: [PATCH 163/929] fix: Total amount not displayed in Journal Entry (#20795) * fix: Total amount not displayed in Journal Entry * fix: Update paid_to_received field * fix: set total amount Co-authored-by: Nabin Hait --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 428b9acd150..04c5c9ec679 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -459,8 +459,7 @@ class JournalEntry(AccountsController): for d in self.get('accounts'): if d.party_type in ['Customer', 'Supplier'] and d.party: if not pay_to_recd_from: - pay_to_recd_from = frappe.db.get_value(d.party_type, d.party, - "customer_name" if d.party_type=="Customer" else "supplier_name") + pay_to_recd_from = d.party if pay_to_recd_from and pay_to_recd_from == d.party: party_amount += (d.debit_in_account_currency or d.credit_in_account_currency) @@ -471,7 +470,8 @@ class JournalEntry(AccountsController): bank_account_currency = d.account_currency if pay_to_recd_from: - self.pay_to_recd_from = pay_to_recd_from + self.pay_to_recd_from = frappe.db.get_value(d.party_type, pay_to_recd_from, + "customer_name" if d.party_type=="Customer" else "supplier_name") if bank_amount: total_amount = bank_amount currency = bank_account_currency From 7766530afcd30641de2d7497e5a1465f407ea2e1 Mon Sep 17 00:00:00 2001 From: Priyanka Gangar <59438065+0925-pinka@users.noreply.github.com> Date: Mon, 2 Mar 2020 19:24:14 +0530 Subject: [PATCH 164/929] fix: fetch sales person name (#20801) * fix: fetch sales person name * Update sales_person.js Co-authored-by: pinka0925 <44537026+pinka0925@users.noreply.github.com> Co-authored-by: Nabin Hait --- erpnext/setup/doctype/sales_person/sales_person.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/setup/doctype/sales_person/sales_person.js b/erpnext/setup/doctype/sales_person/sales_person.js index 9ff37fa4e83..89ca4a9dd74 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.js +++ b/erpnext/setup/doctype/sales_person/sales_person.js @@ -19,6 +19,11 @@ frappe.ui.form.on('Sales Person', { } } }; + + frm.make_methods = { + 'Sales Order': () => frappe.new_doc("Sales Order") + .then(() => frm.add_child("sales_team", {"sales_person": frm.doc.name})) + } } }); From 2728590471d850dab7df1ef0e76655b4de428881 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 4 Mar 2020 10:23:49 +0530 Subject: [PATCH 165/929] fix: HSN code no visible in GST itemised sales register --- .../gst_itemised_sales_register/gst_itemised_sales_register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py b/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py index ab523e74e67..e13f509f475 100644 --- a/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py +++ b/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py @@ -15,7 +15,7 @@ def execute(filters=None): dict(fieldtype='Data', label='GST Category', fieldname="gst_category", width=120), dict(fieldtype='Data', label='Export Type', fieldname="export_type", width=120), dict(fieldtype='Data', label='E-Commerce GSTIN', fieldname="ecommerce_gstin", width=130), - dict(fieldtype='Data', label='HSN Code', fieldname="hsn_code", width=120) + dict(fieldtype='Data', label='HSN Code', fieldname="gst_hsn_code", width=120) ], additional_query_columns=[ 'customer_gstin', 'billing_address_gstin', From 12cdf0fb5242a3a7de10775174d66dfa0aa8a6ad Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Thu, 5 Mar 2020 10:08:43 +0530 Subject: [PATCH 166/929] fix: use ERPNext in welcome email when default company is not set Signed-off-by: Chinmay D. Pai --- erpnext/setup/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index d1c206d8b1d..ffd5ab1e840 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -141,6 +141,6 @@ def insert_record(records): raise def welcome_email(): - site_name = get_default_company() - title = _("Welcome to {0}".format(site_name)) + site_name = get_default_company() or "ERPNext" + title = _("Welcome to {0}").format(site_name) return title From 1f80c8dffef3d779a32fa3e9eb12b5517cb167e7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 5 Mar 2020 13:04:53 +0530 Subject: [PATCH 167/929] perf: improve gl entry submission (#20802) * perf: improve gl entry submission * perf: add indexes * fix: replace **kwargs with *args * fix: syntax error * fix: travis * chore: remove purchase invoice from status updater * fix: set_staus args Co-Authored-By: Nabin Hait * fix: only update status for invoices & fees * [bug][fix]: set status to object instead of variable (#20790) Co-authored-by: Nabin Hait Co-authored-by: Saurabh --- .../discounted_invoice.json | 5 +- .../discounted_invoice/discounted_invoice.py | 2 +- erpnext/accounts/doctype/gl_entry/gl_entry.py | 30 +++- .../payment_entry/test_payment_entry.py | 45 +++++- .../purchase_invoice/purchase_invoice.py | 49 +++++++ .../doctype/sales_invoice/sales_invoice.py | 128 +++++++++++------- erpnext/accounts/general_ledger.py | 5 +- erpnext/controllers/status_updater.py | 11 -- 8 files changed, 212 insertions(+), 63 deletions(-) diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json index 5c3519a1592..02b0c4d937b 100644 --- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json +++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json @@ -18,7 +18,8 @@ "in_list_view": 1, "label": "Invoice", "options": "Sales Invoice", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "fetch_from": "sales_invoice.customer", @@ -60,7 +61,7 @@ } ], "istable": 1, - "modified": "2019-09-26 11:05:36.016772", + "modified": "2020-02-20 16:16:20.724620", "modified_by": "Administrator", "module": "Accounts", "name": "Discounted Invoice", diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py index 93dfcc14bda..109737f7276 100644 --- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py +++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py @@ -7,4 +7,4 @@ from __future__ import unicode_literals from frappe.model.document import Document class DiscountedInvoice(Document): - pass + pass \ No newline at end of file diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 041e419752b..f9e4fd77148 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -232,11 +232,36 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga if bal < 0 and not on_cancel: frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))) - # Update outstanding amt on against voucher if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]: + update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal) + +def update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal): + data = [] + # Update outstanding amt on against voucher + if against_voucher_type == "Fees": ref_doc = frappe.get_doc(against_voucher_type, against_voucher) ref_doc.db_set('outstanding_amount', bal) ref_doc.set_status(update=True) + return + elif against_voucher_type == "Purchase Invoice": + from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_status + data = frappe.db.get_value(against_voucher_type, against_voucher, + ["name as purchase_invoice", "outstanding_amount", + "is_return", "due_date", "docstatus"]) + elif against_voucher_type == "Sales Invoice": + from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_status + data = frappe.db.get_value(against_voucher_type, against_voucher, + ["name as sales_invoice", "outstanding_amount", "is_discounted", + "is_return", "due_date", "docstatus"]) + + precision = frappe.get_precision(against_voucher_type, "outstanding_amount") + data = list(data) + data.append(precision) + status = get_status(data) + frappe.db.set_value(against_voucher_type, against_voucher, { + 'outstanding_amount': bal, + 'status': status + }) def validate_frozen_account(account, adv_adj=None): frozen_account = frappe.db.get_value("Account", account, "freeze_account") @@ -274,6 +299,9 @@ def update_against_account(voucher_type, voucher_no): if d.against != new_against: frappe.db.set_value("GL Entry", d.name, "against", new_against) +def on_doctype_update(): + frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"]) + frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"]) def rename_gle_sle_docs(): for doctype in ["GL Entry", "Stock Ledger Entry"]: diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index a7ab1754bfc..5303743d424 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -149,6 +149,49 @@ class TestPaymentEntry(unittest.TestCase): outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount")) self.assertEqual(outstanding_amount, 0) + + def test_payment_against_sales_invoice_to_check_status(self): + si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", + currency="USD", conversion_rate=50) + + pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") + pe.reference_no = "1" + pe.reference_date = "2016-01-01" + pe.target_exchange_rate = 50 + pe.insert() + pe.submit() + + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 0) + self.assertEqual(si.status, 'Paid') + + pe.cancel() + + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 100) + self.assertEqual(si.status, 'Unpaid') + + def test_payment_against_purchase_invoice_to_check_status(self): + pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC", + currency="USD", conversion_rate=50) + + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC") + pe.reference_no = "1" + pe.reference_date = "2016-01-01" + pe.source_exchange_rate = 50 + pe.insert() + pe.submit() + + outstanding_amount = flt(frappe.db.get_value("Purchase Invoice", pi.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 0) + self.assertEqual(pi.status, 'Paid') + + pe.cancel() + + outstanding_amount = flt(frappe.db.get_value("Purchase Invoice", pi.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 100) + self.assertEqual(pi.status, 'Unpaid') + def test_payment_entry_against_ec(self): payable = frappe.get_cached_value('Company', "_Test Company", 'default_payable_account') @@ -566,4 +609,4 @@ class TestPaymentEntry(unittest.TestCase): self.assertEqual(expected_party_account_balance, party_account_balance) accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() + accounts_settings.save() \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 518c4d36af4..754a97558b3 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -125,6 +125,27 @@ class PurchaseInvoice(BuyingController): else: self.remarks = _("No Remarks") + def set_status(self, update=False, status=None, update_modified=True): + if self.is_new(): + if self.get('amended_from'): + self.status = 'Draft' + return + + if not status: + precision = self.precision("outstanding_amount") + args = [ + self.name, + self.outstanding_amount, + self.is_return, + self.due_date, + self.docstatus, + precision + ] + self.status = get_status(args) + + if update: + self.db_set('status', self.status, update_modified = update_modified) + def set_missing_values(self, for_validate=False): if not self.credit_to: self.credit_to = get_party_account("Supplier", self.supplier, self.company) @@ -964,6 +985,34 @@ class PurchaseInvoice(BuyingController): # calculate totals again after applying TDS self.calculate_taxes_and_totals() +def get_status(*args): + purchase_invoice, outstanding_amount, is_return, due_date, docstatus, precision = args[0] + + outstanding_amount = flt(outstanding_amount, precision) + due_date = getdate(due_date) + now_date = getdate() + + if docstatus == 2: + status = "Cancelled" + elif docstatus == 1: + if outstanding_amount > 0 and due_date < now_date: + status = "Overdue" + elif outstanding_amount > 0 and due_date >= now_date: + status = "Unpaid" + #Check if outstanding amount is 0 due to debit note issued against invoice + elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': purchase_invoice, 'docstatus': 1}): + status = "Debit Note Issued" + elif is_return == 1: + status = "Return" + elif outstanding_amount <=0: + status = "Paid" + else: + status = "Submitted" + else: + status = "Draft" + + return status + def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 8258833254b..ed34127fbb3 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1211,63 +1211,84 @@ class SalesInvoice(SellingController): self.set_missing_values(for_validate = True) - def get_discounting_status(self): - status = None - if self.is_discounted: - invoice_discounting_list = frappe.db.sql(""" - select status - from `tabInvoice Discounting` id, `tabDiscounted Invoice` d - where - id.name = d.parent - and d.sales_invoice=%s - and id.docstatus=1 - and status in ('Disbursed', 'Settled') - """, self.name) - for d in invoice_discounting_list: - status = d[0] - if status == "Disbursed": - break - return status - def set_status(self, update=False, status=None, update_modified=True): if self.is_new(): if self.get('amended_from'): self.status = 'Draft' return - precision = self.precision("outstanding_amount") - outstanding_amount = flt(self.outstanding_amount, precision) - due_date = getdate(self.due_date) - nowdate = getdate() - discountng_status = self.get_discounting_status() - if not status: - if self.docstatus == 2: - status = "Cancelled" - elif self.docstatus == 1: - if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed': - self.status = "Overdue and Discounted" - elif outstanding_amount > 0 and due_date < nowdate: - self.status = "Overdue" - elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed': - self.status = "Unpaid and Discounted" - elif outstanding_amount > 0 and due_date >= nowdate: - self.status = "Unpaid" - #Check if outstanding amount is 0 due to credit note issued against invoice - elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): - self.status = "Credit Note Issued" - elif self.is_return == 1: - self.status = "Return" - elif outstanding_amount<=0: - self.status = "Paid" - else: - self.status = "Submitted" - else: - self.status = "Draft" + precision = self.precision("outstanding_amount") + args = [ + self.name, + self.outstanding_amount, + self.is_discounted, + self.is_return, + self.due_date, + self.docstatus, + precision, + ] + self.status = get_status(args) if update: self.db_set('status', self.status, update_modified = update_modified) +def get_discounting_status(sales_invoice): + status = None + + invoice_discounting_list = frappe.db.sql(""" + select status + from `tabInvoice Discounting` id, `tabDiscounted Invoice` d + where + id.name = d.parent + and d.sales_invoice=%s + and id.docstatus=1 + and status in ('Disbursed', 'Settled') + """, sales_invoice) + + for d in invoice_discounting_list: + status = d[0] + if status == "Disbursed": + break + + return status + +def get_status(*args): + sales_invoice, outstanding_amount, is_discounted, is_return, due_date, docstatus, precision = args[0] + + discounting_status = None + if is_discounted: + discounting_status = get_discounting_status(sales_invoice) + + outstanding_amount = flt(outstanding_amount, precision) + due_date = getdate(due_date) + now_date = getdate() + + if docstatus == 2: + status = "Cancelled" + elif docstatus == 1: + if outstanding_amount > 0 and due_date < now_date and is_discounted and discounting_status=='Disbursed': + status = "Overdue and Discounted" + elif outstanding_amount > 0 and due_date < now_date: + status = "Overdue" + elif outstanding_amount > 0 and due_date >= now_date and is_discounted and discounting_status=='Disbursed': + status = "Unpaid and Discounted" + elif outstanding_amount > 0 and due_date >= now_date: + status = "Unpaid" + #Check if outstanding amount is 0 due to credit note issued against invoice + elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': sales_invoice, 'docstatus': 1}): + status = "Credit Note Issued" + elif is_return == 1: + status = "Return" + elif outstanding_amount <=0: + status = "Paid" + else: + status = "Submitted" + else: + status = "Draft" + + return status + def validate_inter_company_party(doctype, party, company, inter_company_reference): if not party: return @@ -1435,6 +1456,21 @@ def get_inter_company_details(doc, doctype): "company": company } +def get_internal_party(parties, link_doctype, doc): + if len(parties) == 1: + party = parties[0].name + else: + # If more than one Internal Supplier/Customer, get supplier/customer on basis of address + if doc.get('company_address') or doc.get('shipping_address'): + party = frappe.db.get_value("Dynamic Link", {"parent": doc.get('company_address') or doc.get('shipping_address'), + "parenttype": "Address", "link_doctype": link_doctype}, "link_name") + + if not party: + party = parties[0].name + else: + party = parties[0].name + + return party def validate_inter_company_transaction(doc, doctype): diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index bb1b7e392dc..6d53530321f 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -140,8 +140,11 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False): gle = frappe.get_doc(args) gle.flags.ignore_permissions = 1 gle.flags.from_repost = from_repost - gle.insert() + gle.validate() + gle.flags.ignore_permissions = True + gle.db_insert() gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost) + gle.flags.ignore_validate = True gle.submit() def validate_account_for_perpetual_inventory(gl_map): diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 8b275a64fb3..b465a106f0e 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -44,17 +44,6 @@ status_map = { ["Closed", "eval:self.status=='Closed'"], ["On Hold", "eval:self.status=='On Hold'"], ], - "Purchase Invoice": [ - ["Draft", None], - ["Submitted", "eval:self.docstatus==1"], - ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"], - ["Return", "eval:self.is_return==1 and self.docstatus==1"], - ["Debit Note Issued", - "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], - ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], - ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], - ["Cancelled", "eval:self.docstatus==2"], - ], "Purchase Order": [ ["Draft", None], ["To Receive and Bill", "eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1"], From 69d3c4ff254aeeb5f9279220baf6443b447af272 Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 6 Mar 2020 10:57:43 +0530 Subject: [PATCH 168/929] fix: update_child_qty_rate() missing argument (#20839) --- erpnext/public/js/utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 3f444f83879..98986f8bf1b 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -439,12 +439,13 @@ erpnext.utils.update_child_items = function(opts) { const dialog = new frappe.ui.Dialog({ title: __("Update Items"), fields: [ - {fieldtype:'Section Break', label: __('Items')}, { fieldname: "trans_items", fieldtype: "Table", + label: "Items", cannot_add_rows: cannot_add_row, in_place_edit: true, + reqd: 1, data: this.data, get_data: () => { return this.data; From 6c4cddccf6c9bec81c533aaf72865998e42de72d Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 6 Mar 2020 11:06:41 +0530 Subject: [PATCH 169/929] fix: show uom in print formats instead of stock uom (#20844) Co-authored-by: Saqib Ansari --- .../purchase_invoice_item/purchase_invoice_item.json | 3 +-- .../selling/doctype/quotation_item/quotation_item.json | 8 ++++++-- .../doctype/sales_order_item/sales_order_item.json | 4 ++-- .../doctype/delivery_note_item/delivery_note_item.json | 6 ++++-- .../purchase_receipt_item/purchase_receipt_item.json | 4 ++-- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index acb0398b5c0..36bb2be3dc5 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -198,7 +198,6 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", - "print_hide": 1, "reqd": 1 }, { @@ -771,7 +770,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2019-12-04 12:23:17.046413", + "modified": "2020-03-05 14:20:17.297284", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index a299b8809ab..3ff5555e827 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2013-03-07 11:42:57", "doctype": "DocType", "document_type": "Document", @@ -163,6 +164,7 @@ "oldfieldname": "stock_uom", "oldfieldtype": "Data", "options": "UOM", + "print_hide": 1, "print_width": "100px", "read_only": 1, "width": "100px" @@ -176,7 +178,6 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", - "print_hide": 1, "reqd": 1 }, { @@ -382,6 +383,7 @@ "read_only": 1 }, { + "default": "0", "fieldname": "is_free_item", "fieldtype": "Check", "label": "Is Free Item", @@ -517,6 +519,7 @@ }, { "allow_on_submit": 1, + "default": "0", "fieldname": "page_break", "fieldtype": "Check", "label": "Page Break", @@ -574,7 +577,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-05-01 17:39:14.228141", + "links": [], + "modified": "2020-03-05 14:18:58.783751", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Item", diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 317f284eff3..c95f236ebba 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -209,7 +209,6 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", - "print_hide": 1, "reqd": 1 }, { @@ -758,7 +757,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-12-11 18:06:26.238169", + "links": [], + "modified": "2020-03-05 14:20:28.085117", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index af06daeabd3..782ac84e57d 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-04-22 13:15:44", "doctype": "DocType", @@ -180,6 +181,7 @@ "oldfieldname": "stock_uom", "oldfieldtype": "Data", "options": "UOM", + "print_hide": 1, "print_width": "50px", "read_only": 1, "reqd": 1, @@ -195,7 +197,6 @@ "in_list_view": 1, "label": "UOM", "options": "UOM", - "print_hide": 1, "reqd": 1 }, { @@ -702,7 +703,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-05-25 22:08:27.452734", + "links": [], + "modified": "2020-03-05 14:18:33.131672", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 16ec8db335c..d5cf4cadb14 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -222,7 +222,6 @@ "oldfieldname": "uom", "oldfieldtype": "Link", "options": "UOM", - "print_hide": 1, "print_width": "100px", "reqd": 1, "width": "100px" @@ -823,7 +822,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-10-14 16:03:25.499557", + "links": [], + "modified": "2020-03-05 14:19:48.799370", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From d9ab412032602f40cc6f78a1c355c0a85c3989f5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 6 Mar 2020 19:04:41 +0530 Subject: [PATCH 170/929] feat: Allow PI creation without PO --- .../purchase_invoice/purchase_invoice.py | 16 +- erpnext/buying/doctype/supplier/supplier.json | 2002 ++++------------- 2 files changed, 396 insertions(+), 1622 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 737c18a8a98..19edd060a8e 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, erpnext -from frappe.utils import cint, cstr, formatdate, flt, getdate, nowdate +from frappe.utils import cint, cstr, formatdate, flt, getdate, nowdate, get_link_to_form from frappe import _, throw import frappe.defaults @@ -255,16 +255,26 @@ class PurchaseInvoice(BuyingController): def po_required(self): if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes': + + if frappe.get_value('Supplier', self.supplier, 'allow_purchase_invoice_creation_without_purchase_order'): + return + for d in self.get('items'): if not d.purchase_order: - throw(_("As per the Buying Settings if Purchase Order Required == 'YES', then for creating Purchase Invoice, user need to create Purchase Order first for item {0}").format(d.item_code)) + throw(_("""Purchase Order Required for item {0} + To submit the invoice without purchase order please set + {1} as {2} in {3}""").format(frappe.bold(d.item_code), frappe.bold('Purchase Order Required'), + frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))) def pr_required(self): stock_items = self.get_stock_items() if frappe.db.get_value("Buying Settings", None, "pr_required") == 'Yes': for d in self.get('items'): if not d.purchase_receipt and d.item_code in stock_items: - throw(_("As per the Buying Settings if Purchase Reciept Required == 'YES', then for creating Purchase Invoice, user need to create Purchase Receipt first for item {0}").format(d.item_code)) + throw(_("""Purchase Receipt Required for item {0} + To submit the invoice without purchase receipt please set + {1} as {2} in {3}""").format(frappe.bold(d.item_code), frappe.bold('Purchase Receipt Required'), + frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))) def validate_write_off_account(self): if self.write_off_amount and not self.write_off_account: diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index b5f0366c8e4..6ad238d2b38 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -1,1683 +1,447 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 1, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "naming_series:", - "beta": 0, - "creation": "2013-01-10 16:34:11", - "custom": 0, - "description": "Supplier of Goods or Services.", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "engine": "InnoDB", + "actions": [], + "allow_events_in_timeline": 1, + "allow_import": 1, + "allow_rename": 1, + "autoname": "naming_series:", + "creation": "2013-01-10 16:34:11", + "description": "Supplier of Goods or Services.", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "basic_info", + "naming_series", + "supplier_name", + "country", + "default_bank_account", + "tax_id", + "tax_category", + "tax_withholding_category", + "is_transporter", + "is_internal_supplier", + "represents_company", + "image", + "column_break0", + "supplier_group", + "supplier_type", + "pan", + "language", + "allow_purchase_invoice_creation_without_purchase_order", + "disabled", + "warn_rfqs", + "warn_pos", + "prevent_rfqs", + "prevent_pos", + "allowed_to_transact_section", + "companies", + "section_break_7", + "default_currency", + "column_break_10", + "default_price_list", + "section_credit_limit", + "payment_terms", + "cb_21", + "on_hold", + "hold_type", + "release_date", + "address_contacts", + "address_html", + "column_break1", + "contact_html", + "default_payable_accounts", + "accounts", + "default_tax_withholding_config", + "column_break2", + "website", + "supplier_details", + "column_break_30", + "is_frozen" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "basic_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Name and Type", - "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 - }, + "fieldname": "basic_info", + "fieldtype": "Section Break", + "label": "Name and Type", + "oldfieldtype": "Section Break", + "options": "fa fa-user" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 1, - "oldfieldname": "naming_series", - "oldfieldtype": "Select", - "options": "SUP-.YYYY.-", - "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": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "oldfieldname": "naming_series", + "oldfieldtype": "Select", + "options": "SUP-.YYYY.-", + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fieldname": "supplier_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": "Supplier Name", - "length": 0, - "no_copy": 1, - "oldfieldname": "supplier_name", - "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": 0 - }, + "bold": 1, + "fieldname": "supplier_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Supplier Name", + "no_copy": 1, + "oldfieldname": "supplier_name", + "oldfieldtype": "Data", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "country", + "fieldtype": "Link", + "label": "Country", + "options": "Country" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_bank_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Bank Account", - "length": 0, - "no_copy": 0, - "options": "Bank Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "default_bank_account", + "fieldtype": "Link", + "label": "Default Bank Account", + "options": "Bank Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tax_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax ID", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "tax_id", + "fieldtype": "Data", + "label": "Tax ID" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tax_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax Category", - "length": 0, - "no_copy": 0, - "options": "Tax Category", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "tax_category", + "fieldtype": "Link", + "label": "Tax Category", + "options": "Tax Category" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tax_withholding_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax Withholding Category", - "length": 0, - "no_copy": 0, - "options": "Tax Withholding Category", - "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": "tax_withholding_category", + "fieldtype": "Link", + "label": "Tax Withholding Category", + "options": "Tax Withholding Category" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_transporter", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Transporter", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "is_transporter", + "fieldtype": "Check", + "label": "Is Transporter" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "is_internal_supplier", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Internal Supplier", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "is_internal_supplier", + "fieldtype": "Check", + "label": "Is Internal Supplier" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_internal_supplier", - "fieldname": "represents_company", - "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": "Represents Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "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, + "depends_on": "is_internal_supplier", + "fieldname": "represents_company", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Represents Company", + "options": "Company", "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "image", - "fieldtype": "Attach Image", - "hidden": 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": "Image", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "image", + "fieldtype": "Attach Image", + "hidden": 1, + "label": "Image", + "no_copy": 1, + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break0", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "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_break0", + "fieldtype": "Column Break", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "supplier_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Supplier Group", - "length": 0, - "no_copy": 0, - "oldfieldname": "supplier_type", - "oldfieldtype": "Link", - "options": "Supplier Group", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "supplier_group", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Supplier Group", + "oldfieldname": "supplier_type", + "oldfieldtype": "Link", + "options": "Supplier Group", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Company", - "fieldname": "supplier_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": "Supplier Type", - "length": 0, - "no_copy": 0, - "options": "Company\nIndividual", - "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 - }, + "default": "Company", + "fieldname": "supplier_type", + "fieldtype": "Select", + "label": "Supplier Type", + "options": "Company\nIndividual", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "pan", - "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": "PAN", - "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": "pan", + "fieldtype": "Data", + "label": "PAN" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "language", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Language", - "length": 0, - "no_copy": 0, - "options": "Language", - "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": "language", + "fieldtype": "Link", + "label": "Print Language", + "options": "Language" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "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": 0, - "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 - }, + "bold": 1, + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "warn_rfqs", - "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": "Warn RFQs", - "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 - }, + "default": "0", + "fieldname": "warn_rfqs", + "fieldtype": "Check", + "label": "Warn RFQs", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "warn_pos", - "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": "Warn POs", - "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 - }, + "default": "0", + "fieldname": "warn_pos", + "fieldtype": "Check", + "label": "Warn POs", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "prevent_rfqs", - "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": "Prevent RFQs", - "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 - }, + "default": "0", + "fieldname": "prevent_rfqs", + "fieldtype": "Check", + "label": "Prevent RFQs", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "prevent_pos", - "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": "Prevent POs", - "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 - }, + "default": "0", + "fieldname": "prevent_pos", + "fieldtype": "Check", + "label": "Prevent POs", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "represents_company", - "fieldname": "allowed_to_transact_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": "Allowed To Transact With", - "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 - }, + "depends_on": "represents_company", + "fieldname": "allowed_to_transact_section", + "fieldtype": "Section Break", + "label": "Allowed To Transact With" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "represents_company", - "fieldname": "companies", - "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": "Allowed To Transact With", - "length": 0, - "no_copy": 0, - "options": "Allowed To Transact With", - "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 - }, + "depends_on": "represents_company", + "fieldname": "companies", + "fieldtype": "Table", + "label": "Allowed To Transact With", + "options": "Allowed To Transact With" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "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, - "label": "Currency and Price 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 - }, + "collapsible": 1, + "fieldname": "section_break_7", + "fieldtype": "Section Break", + "label": "Currency and Price List" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_currency", - "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": "Billing Currency", - "length": 0, - "no_copy": 1, - "options": "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 - }, + "fieldname": "default_currency", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Billing Currency", + "no_copy": 1, + "options": "Currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 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 - }, + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_price_list", - "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": "Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "default_price_list", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Price List", + "options": "Price List" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "section_credit_limit", - "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": "Credit Limit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "section_credit_limit", + "fieldtype": "Section Break", + "label": "Credit Limit" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "payment_terms", - "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 Payment Terms Template", - "length": 0, - "no_copy": 0, - "options": "Payment Terms 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": "payment_terms", + "fieldtype": "Link", + "label": "Default Payment Terms Template", + "options": "Payment Terms Template" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_21", - "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": "cb_21", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "on_hold", - "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": "Block Supplier", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "on_hold", + "fieldtype": "Check", + "label": "Block Supplier" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.on_hold", - "fieldname": "hold_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": "Hold Type", - "length": 0, - "no_copy": 0, - "options": "\nAll\nInvoices\nPayments", - "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 - }, + "depends_on": "eval:doc.on_hold", + "fieldname": "hold_type", + "fieldtype": "Select", + "label": "Hold Type", + "options": "\nAll\nInvoices\nPayments" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.on_hold", - "description": "Leave blank if the Supplier is blocked indefinitely", - "fieldname": "release_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": "Release 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 - }, + "depends_on": "eval:doc.on_hold", + "description": "Leave blank if the Supplier is blocked indefinitely", + "fieldname": "release_date", + "fieldtype": "Date", + "label": "Release Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "fieldname": "address_contacts", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Address and Contacts", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "options": "fa fa-map-marker", - "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 - }, + "depends_on": "eval:!doc.__islocal", + "fieldname": "address_contacts", + "fieldtype": "Section Break", + "label": "Address and Contacts", + "oldfieldtype": "Column Break", + "options": "fa fa-map-marker" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_html", - "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": "Address HTML", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "address_html", + "fieldtype": "HTML", + "label": "Address HTML", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "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", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_html", - "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": "Contact HTML", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_html", + "fieldtype": "HTML", + "label": "Contact HTML", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "accounts", - "columns": 0, - "fieldname": "default_payable_accounts", - "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": "Default Payable Accounts", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "accounts", + "fieldname": "default_payable_accounts", + "fieldtype": "Section Break", + "label": "Default Payable Accounts" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "Mention if non-standard payable account", - "fieldname": "accounts", - "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": "Accounts", - "length": 0, - "no_copy": 0, - "options": "Party Account", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Mention if non-standard payable account", + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounts", + "options": "Party Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "default_tax_withholding_config", - "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": "Default Tax Withholding Config", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "default_tax_withholding_config", + "fieldtype": "Section Break", + "label": "Default Tax Withholding Config" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "supplier_details", - "columns": 0, - "fieldname": "column_break2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "collapsible": 1, + "collapsible_depends_on": "supplier_details", + "fieldname": "column_break2", + "fieldtype": "Section Break", + "label": "More Information", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 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, - "oldfieldname": "website", - "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": "website", + "fieldtype": "Data", + "label": "Website", + "oldfieldname": "website", + "oldfieldtype": "Data" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Statutory info and other general information about your Supplier", - "fieldname": "supplier_details", - "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": "Supplier Details", - "length": 0, - "no_copy": 0, - "oldfieldname": "supplier_details", - "oldfieldtype": "Code", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Statutory info and other general information about your Supplier", + "fieldname": "supplier_details", + "fieldtype": "Text", + "label": "Supplier Details", + "oldfieldname": "supplier_details", + "oldfieldtype": "Code" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_30", - "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_30", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_frozen", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Frozen", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "is_frozen", + "fieldtype": "Check", + "label": "Is Frozen" + }, + { + "default": "0", + "fieldname": "allow_purchase_invoice_creation_without_purchase_order", + "fieldtype": "Check", + "label": "Allow Purchase Invoice creation without Purchase Order" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-user", - "idx": 370, - "image_field": "image", - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-01-17 13:58:08.597793", - "modified_by": "Administrator", - "module": "Buying", - "name": "Supplier", - "name_case": "Title Case", - "owner": "Administrator", + ], + "icon": "fa fa-user", + "idx": 370, + "image_field": "image", + "links": [], + "modified": "2020-03-06 19:04:06.097092", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier", + "name_case": "Title Case", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase Manager", "write": 1 - }, + }, { - "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": "Purchase Master Manager", - "set_user_permissions": 1, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase Master Manager", + "set_user_permissions": 1, + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "read": 1, + "role": "Stock User" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager" + }, { - "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": "Accounts User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "read": 1, + "role": "Accounts User" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager" } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "supplier_name, supplier_group", - "show_name_in_global_search": 1, - "sort_order": "ASC", - "title_field": "supplier_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} + ], + "quick_entry": 1, + "search_fields": "supplier_name, supplier_group", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "ASC", + "title_field": "supplier_name", + "track_changes": 1 +} \ No newline at end of file From 875e0f5fb23589f2e44ffbf344a1f01cf37d1f7b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 9 Mar 2020 08:06:07 +0530 Subject: [PATCH 171/929] fix: Acccounting Dimension disappearing in first row after refresh --- erpnext/public/js/utils/dimension_tree_filter.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index 7abe4a7aab1..8b6de0f5179 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -35,11 +35,13 @@ erpnext.doctypes_with_dimensions.forEach((doctype) => { } } - if (frm.doc.items && frm.doc.items.length) { + if (frm.doc.items && frm.doc.items.length && frm.doc.docstatus === 0 + && (!frm.doc.items[0][dimension['fieldname']])) { frm.doc.items[0][dimension['fieldname']] = erpnext.default_dimensions[frm.doc.company][dimension['document_type']]; } - if (frm.doc.accounts && frm.doc.accounts.length) { + if (frm.doc.accounts && frm.doc.accounts.length && frm.doc.docstatus === 0 + && (!frm.doc.items[0][dimension['fieldname']])) { frm.doc.accounts[0][dimension['fieldname']] = erpnext.default_dimensions[frm.doc.company][dimension['document_type']]; } } From 2f2cef6c5f8574b5e928280436aec0fb80b13588 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 9 Mar 2020 18:07:55 +0530 Subject: [PATCH 172/929] fix: ModuleNotFoundError on Turkey Company setup (#20854) --- erpnext/regional/turkey/setup.py | 4 ++++ erpnext/setup/doctype/company/company.py | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 erpnext/regional/turkey/setup.py diff --git a/erpnext/regional/turkey/setup.py b/erpnext/regional/turkey/setup.py new file mode 100644 index 00000000000..ebf3b2bee1f --- /dev/null +++ b/erpnext/regional/turkey/setup.py @@ -0,0 +1,4 @@ +from __future__ import unicode_literals + +def setup(company=None, patch=True): + pass \ No newline at end of file diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index ff3515485c4..c3fd3566578 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -416,8 +416,13 @@ def install_country_fixtures(company): company_doc = frappe.get_doc("Company", company) path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(company_doc.country)) if os.path.exists(path.encode("utf-8")): - frappe.get_attr("erpnext.regional.{0}.setup.setup" - .format(frappe.scrub(company_doc.country)))(company_doc, False) + try: + module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(company_doc.country)) + frappe.get_attr(module_name)(company_doc, False) + except Exception as e: + frappe.log_error(str(e), frappe.get_traceback()) + frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(company_doc.country))) + def update_company_current_month_sales(company): current_month_year = formatdate(today(), "MM-yyyy") From d08cff9efc35892980496b11a913a1ab836e64e1 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 9 Mar 2020 18:13:38 +0530 Subject: [PATCH 173/929] fix: Validate Serial No/Batch No against unserialized Item in Stock Reconciliation (#20859) --- .../stock/doctype/stock_reconciliation/stock_reconciliation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index ca2741ccfba..afa239466b5 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -190,6 +190,9 @@ class StockReconciliation(StockController): has_serial_no = True self.get_sle_for_serialized_items(row, sl_entries) else: + if row.serial_no or row.batch_no: + frappe.throw(_("Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.") \ + .format(row.idx, frappe.bold(row.item_code))) previous_sle = get_previous_sle({ "item_code": row.item_code, "warehouse": row.warehouse, From b607963fba27e92224ce8a40b32443d33717643f Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 9 Mar 2020 18:30:42 +0530 Subject: [PATCH 174/929] fix: [pos] customer group filter resets on syncing offline invoices (#20874) --- erpnext/accounts/doctype/sales_invoice/pos.py | 9 ++++++--- erpnext/accounts/page/pos/pos.js | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 749816f7814..c0e128567f9 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -207,7 +207,7 @@ def get_customers_list(pos_profile={}): if pos_profile.get('customer_groups'): # Get customers based on the customer groups defined in the POS profile for d in pos_profile.get('customer_groups'): - customer_groups.extend([d.name for d in get_child_nodes('Customer Group', d.customer_group)]) + customer_groups.extend([d.get('name') for d in get_child_nodes('Customer Group', d.get('customer_group'))]) cond = "customer_group in (%s)" % (', '.join(['%s'] * len(customer_groups))) return frappe.db.sql(""" select name, customer_name, customer_group, @@ -387,7 +387,9 @@ def get_pricing_rule_data(doc): @frappe.whitelist() -def make_invoice(doc_list={}, email_queue_list={}, customers_list={}): +def make_invoice(pos_profile, doc_list={}, email_queue_list={}, customers_list={}): + import json + if isinstance(doc_list, string_types): doc_list = json.loads(doc_list) @@ -421,7 +423,8 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}): name_list.append(name) email_queue = make_email_queue(email_queue_list) - customers = get_customers_list() + pos_profile = json.loads(pos_profile) + customers = get_customers_list(pos_profile) return { 'invoice': name_list, 'email_queue': email_queue, diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 8dc00f3f218..24fcb41a5db 100755 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -1769,6 +1769,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice", freeze: true, args: { + pos_profile: me.pos_profile_data, doc_list: me.si_docs, email_queue_list: me.email_queue_list, customers_list: me.customers_list From 0317576434c948ebbeb03018e7d56b843dfcd6fd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 9 Mar 2020 22:38:41 +0530 Subject: [PATCH 175/929] fix: HSN code not visible in GST Itemised Purchase register --- .../item_wise_purchase_register.py | 4 ++-- .../gst_itemised_purchase_register.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 4523f66deb4..11f4e0d4e85 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -54,8 +54,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum 'description': d.description, 'invoice': d.parent, 'posting_date': d.posting_date, - 'customer': d.supplier, - 'customer_name': d.supplier_name + 'supplier': d.supplier, + 'supplier_name': d.supplier_name } if additional_query_columns: diff --git a/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py b/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py index 3ce2547b20f..b5948f9952d 100644 --- a/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py +++ b/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py @@ -13,9 +13,9 @@ def execute(filters=None): dict(fieldtype='Data', label='GST Category', fieldname="gst_category", width=120), dict(fieldtype='Data', label='Export Type', fieldname="export_type", width=120), dict(fieldtype='Data', label='E-Commerce GSTIN', fieldname="ecommerce_gstin", width=130), - dict(fieldtype='Data', label='HSN Code', fieldname="hsn_code", width=120), - dict(fieldtype='Data', label='Supplier Invoice No', fieldname="supplier_invoice_no", width=120), - dict(fieldtype='Date', label='Supplier Invoice Date', fieldname="supplier_invoice_date", width=100) + dict(fieldtype='Data', label='HSN Code', fieldname="gst_hsn_code", width=120), + dict(fieldtype='Data', label='Supplier Invoice No', fieldname="bill_no", width=120), + dict(fieldtype='Date', label='Supplier Invoice Date', fieldname="bill_date", width=100) ], additional_query_columns=[ 'supplier_gstin', 'company_gstin', From ee003495f6470c88fc70192e60def6de07370fed Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Mar 2020 10:36:05 +0530 Subject: [PATCH 176/929] fix: Get invoice details only if invoice is selected --- erpnext/accounts/doctype/c_form/c_form.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/c_form/c_form.js b/erpnext/accounts/doctype/c_form/c_form.js index 3d0fc0a42ce..846408ff244 100644 --- a/erpnext/accounts/doctype/c_form/c_form.js +++ b/erpnext/accounts/doctype/c_form/c_form.js @@ -32,10 +32,12 @@ frappe.ui.form.on('C-Form Invoice Detail', { invoice_no(frm, cdt, cdn) { let d = frappe.get_doc(cdt, cdn); - frm.call('get_invoice_details', { - invoice_no: d.invoice_no - }).then(r => { - frappe.model.set_value(cdt, cdn, r.message); - }); + if (d.invoice_no) { + frm.call('get_invoice_details', { + invoice_no: d.invoice_no + }).then(r => { + frappe.model.set_value(cdt, cdn, r.message); + }); + } } }); From 0f807bf7ee2038d4868996ded1eb3025cd36274b Mon Sep 17 00:00:00 2001 From: Myuddin khatri Date: Thu, 12 Mar 2020 14:30:28 +0530 Subject: [PATCH 177/929] fix(shopping-cart): address is made mandatory to place order --- erpnext/shopping_cart/cart.py | 24 +++++++++---------- erpnext/templates/includes/cart.js | 5 ++-- .../templates/includes/cart/cart_address.html | 18 ++++++++++---- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 813d0dd196f..f85abdba04d 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -38,14 +38,14 @@ def get_cart_quotation(doc=None): addresses = get_address_docs(party=party) if not doc.customer_address and addresses: - update_cart_address("customer_address", addresses[0].name) + update_cart_address("billing", addresses[0].name) return { "doc": decorate_quotation_doc(doc), "shipping_addresses": [{"name": address.name, "display": address.display} - for address in addresses], + for address in addresses if address.address_type == "Shipping"], "billing_addresses": [{"name": address.name, "display": address.display} - for address in addresses], + for address in addresses if address.address_type == "Billing"], "shipping_rules": get_applicable_shipping_rules(party), "cart_settings": frappe.get_cached_doc("Shopping Cart Settings") } @@ -64,6 +64,9 @@ def place_order(): # company used to create customer accounts frappe.defaults.set_user_default("company", quotation.company) + if not (quotation.shipping_address_name or quotation.customer_address): + frappe.throw(_("Set Shipping Address or Billing Address")) + from erpnext.selling.doctype.quotation.quotation import _make_sales_order sales_order = frappe.get_doc(_make_sales_order(quotation.name, ignore_permissions=True)) sales_order.payment_schedule = [] @@ -194,21 +197,16 @@ def get_terms_and_conditions(terms_name): return frappe.db.get_value('Terms and Conditions', terms_name, 'terms') @frappe.whitelist() -def update_cart_address(address_fieldname, address_name): +def update_cart_address(address_type, address_name): quotation = _get_cart_quotation() address_display = get_address_display(frappe.get_doc("Address", address_name).as_dict()) - if address_fieldname == "shipping_address_name": - quotation.shipping_address_name = address_name - quotation.shipping_address = address_display - - if not quotation.customer_address: - address_fieldname == "customer_address" - - if address_fieldname == "customer_address": + if address_type.lower() == "billing": quotation.customer_address = address_name quotation.address_display = address_display - + elif address_type.lower() == "shipping": + quotation.shipping_address_name = address_name + quotation.shipping_address = address_display apply_cart_settings(quotation=quotation) diff --git a/erpnext/templates/includes/cart.js b/erpnext/templates/includes/cart.js index 456bc7e4136..c6dfd35e29e 100644 --- a/erpnext/templates/includes/cart.js +++ b/erpnext/templates/includes/cart.js @@ -26,15 +26,14 @@ $.extend(shopping_cart, { bind_address_select: function() { $(".cart-addresses").on('click', '.address-card', function(e) { const $card = $(e.currentTarget); - const address_fieldname = $card.closest('[data-fieldname]').attr('data-fieldname'); + const address_type = $card.closest('[data-address-type]').attr('data-address-type'); const address_name = $card.closest('[data-address-name]').attr('data-address-name'); - return frappe.call({ type: "POST", method: "erpnext.shopping_cart.cart.update_cart_address", freeze: true, args: { - address_fieldname, + address_type, address_name }, callback: function(r) { diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html index f7f35483208..60de3af17bf 100644 --- a/erpnext/templates/includes/cart/cart_address.html +++ b/erpnext/templates/includes/cart/cart_address.html @@ -18,7 +18,7 @@
{{ _("Shipping Address") }}
{% for address in shipping_addresses %} -
+
{% include "templates/includes/cart/address_card.html" %}
{% endfor %} @@ -28,7 +28,7 @@
{{ _("Billing Address") }}
{% for address in billing_addresses %} -
+
{% include "templates/includes/cart/address_card.html" %}
{% endfor %} @@ -123,9 +123,19 @@ frappe.ready(() => { primary_action: (values) => { frappe.call('erpnext.shopping_cart.cart.add_new_address', { doc: values }) .then(r => { - d.hide(); - window.location.reload(); + frappe.call({ + method: "erpnext.shopping_cart.cart.update_cart_address", + args: { + address_type: r.message.address_type, + address_name: r.message.name + }, + callback: function (r) { + d.hide(); + window.location.reload(); + } + }); }); + } }) From 9c63b1d2c9e3d44e3df5c3b8e03f6fe745735320 Mon Sep 17 00:00:00 2001 From: Myuddin khatri Date: Thu, 12 Mar 2020 16:04:22 +0530 Subject: [PATCH 178/929] fix(shopping-cart): setting billing and shipping address setting billing and shipping address --- 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 f85abdba04d..4c3ac100d5b 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -65,7 +65,7 @@ def place_order(): frappe.defaults.set_user_default("company", quotation.company) if not (quotation.shipping_address_name or quotation.customer_address): - frappe.throw(_("Set Shipping Address or Billing Address")) + frappe.throw(_("Set Shipping Address or Billing Address")) from erpnext.selling.doctype.quotation.quotation import _make_sales_order sales_order = frappe.get_doc(_make_sales_order(quotation.name, ignore_permissions=True)) @@ -204,9 +204,11 @@ def update_cart_address(address_type, address_name): if address_type.lower() == "billing": quotation.customer_address = address_name quotation.address_display = address_display + quotation.shipping_address_name == quotation.shipping_address_name or address_name elif address_type.lower() == "shipping": quotation.shipping_address_name = address_name quotation.shipping_address = address_display + quotation.customer_address == quotation.customer_address or address_name apply_cart_settings(quotation=quotation) From 97dfe2ac32aa38b0578476b84c278e74908138aa Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 16 Mar 2020 13:47:47 +0530 Subject: [PATCH 179/929] fix: local variable 'benefit_amount' referenced before assignment (#20948) --- .../employee_benefit_application/employee_benefit_application.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py index fea4246d646..feaa92590a3 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py @@ -190,6 +190,7 @@ def get_benefit_component_amount(employee, start_date, end_date, salary_componen component_max_benefit, depends_on_payment_days = frappe.db.get_value("Salary Component", salary_component, ["max_benefit_amount", "depends_on_payment_days"]) + benefit_amount = 0 if benefit_application: benefit_amount = frappe.db.get_value("Employee Benefit Application Detail", {"parent": benefit_application[0][0], "earning_component": salary_component}, "amount") From e2a31f221a0c0977bab5623d3b574eeee402bbfc Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 16 Mar 2020 15:36:10 +0530 Subject: [PATCH 180/929] fix: Multiple fixes for travis (#20950) --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 32 ++------ .../payment_entry/test_payment_entry.py | 24 +++--- .../purchase_invoice/purchase_invoice.py | 52 +------------ erpnext/accounts/doctype/sales_invoice/pos.py | 5 +- .../doctype/sales_invoice/sales_invoice.py | 78 ++++++++----------- .../sales_invoice/test_sales_invoice.py | 10 +-- erpnext/accounts/general_ledger.py | 5 +- erpnext/controllers/status_updater.py | 11 +++ erpnext/setup/doctype/company/test_company.py | 2 +- erpnext/stock/doctype/item/item.py | 11 ++- .../purchase_receipt/test_purchase_receipt.py | 10 ++- 11 files changed, 89 insertions(+), 151 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index f9e4fd77148..512e348c8bf 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -233,35 +233,13 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))) if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]: - update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal) - -def update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal): - data = [] - # Update outstanding amt on against voucher - if against_voucher_type == "Fees": ref_doc = frappe.get_doc(against_voucher_type, against_voucher) - ref_doc.db_set('outstanding_amount', bal) - ref_doc.set_status(update=True) - return - elif against_voucher_type == "Purchase Invoice": - from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_status - data = frappe.db.get_value(against_voucher_type, against_voucher, - ["name as purchase_invoice", "outstanding_amount", - "is_return", "due_date", "docstatus"]) - elif against_voucher_type == "Sales Invoice": - from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_status - data = frappe.db.get_value(against_voucher_type, against_voucher, - ["name as sales_invoice", "outstanding_amount", "is_discounted", - "is_return", "due_date", "docstatus"]) - precision = frappe.get_precision(against_voucher_type, "outstanding_amount") - data = list(data) - data.append(precision) - status = get_status(data) - frappe.db.set_value(against_voucher_type, against_voucher, { - 'outstanding_amount': bal, - 'status': status - }) + # Didn't use db_set for optimisation purpose + ref_doc.outstanding_amount = bal + frappe.db.set_value(against_voucher_type, against_voucher, 'outstanding_amount', bal) + + ref_doc.set_status(update=True) def validate_frozen_account(account, adv_adj=None): frozen_account = frappe.db.get_value("Account", account, "freeze_account") diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 5303743d424..a25e0e32c86 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -161,15 +161,15 @@ class TestPaymentEntry(unittest.TestCase): pe.insert() pe.submit() - outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) - self.assertEqual(outstanding_amount, 0) - self.assertEqual(si.status, 'Paid') + outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"]) + self.assertEqual(flt(outstanding_amount), 0) + self.assertEqual(status, 'Paid') pe.cancel() - outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) - self.assertEqual(outstanding_amount, 100) - self.assertEqual(si.status, 'Unpaid') + outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"]) + self.assertEqual(flt(outstanding_amount), 100) + self.assertEqual(status, 'Unpaid') def test_payment_against_purchase_invoice_to_check_status(self): pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC", @@ -182,15 +182,15 @@ class TestPaymentEntry(unittest.TestCase): pe.insert() pe.submit() - outstanding_amount = flt(frappe.db.get_value("Purchase Invoice", pi.name, "outstanding_amount")) - self.assertEqual(outstanding_amount, 0) - self.assertEqual(pi.status, 'Paid') + outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"]) + self.assertEqual(flt(outstanding_amount), 0) + self.assertEqual(status, 'Paid') pe.cancel() - outstanding_amount = flt(frappe.db.get_value("Purchase Invoice", pi.name, "outstanding_amount")) - self.assertEqual(outstanding_amount, 100) - self.assertEqual(pi.status, 'Unpaid') + outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"]) + self.assertEqual(flt(outstanding_amount), 250) + self.assertEqual(status, 'Unpaid') def test_payment_entry_against_ec(self): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 754a97558b3..698c2ab333d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -125,27 +125,6 @@ class PurchaseInvoice(BuyingController): else: self.remarks = _("No Remarks") - def set_status(self, update=False, status=None, update_modified=True): - if self.is_new(): - if self.get('amended_from'): - self.status = 'Draft' - return - - if not status: - precision = self.precision("outstanding_amount") - args = [ - self.name, - self.outstanding_amount, - self.is_return, - self.due_date, - self.docstatus, - precision - ] - self.status = get_status(args) - - if update: - self.db_set('status', self.status, update_modified = update_modified) - def set_missing_values(self, for_validate=False): if not self.credit_to: self.credit_to = get_party_account("Supplier", self.supplier, self.company) @@ -985,34 +964,6 @@ class PurchaseInvoice(BuyingController): # calculate totals again after applying TDS self.calculate_taxes_and_totals() -def get_status(*args): - purchase_invoice, outstanding_amount, is_return, due_date, docstatus, precision = args[0] - - outstanding_amount = flt(outstanding_amount, precision) - due_date = getdate(due_date) - now_date = getdate() - - if docstatus == 2: - status = "Cancelled" - elif docstatus == 1: - if outstanding_amount > 0 and due_date < now_date: - status = "Overdue" - elif outstanding_amount > 0 and due_date >= now_date: - status = "Unpaid" - #Check if outstanding amount is 0 due to debit note issued against invoice - elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': purchase_invoice, 'docstatus': 1}): - status = "Debit Note Issued" - elif is_return == 1: - status = "Return" - elif outstanding_amount <=0: - status = "Paid" - else: - status = "Submitted" - else: - status = "Draft" - - return status - def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) @@ -1073,3 +1024,6 @@ def block_invoice(name, release_date, hold_comment=None): def make_inter_company_sales_invoice(source_name, target_doc=None): from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction return make_inter_company_transaction("Purchase Invoice", source_name, target_doc) + +def on_doctype_update(): + frappe.db.add_index("Purchase Invoice", ["supplier", "is_return", "return_against"]) \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index c0e128567f9..16e918cc728 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -423,7 +423,10 @@ def make_invoice(pos_profile, doc_list={}, email_queue_list={}, customers_list={ name_list.append(name) email_queue = make_email_queue(email_queue_list) - pos_profile = json.loads(pos_profile) + + if isinstance(pos_profile, string_types): + pos_profile = json.loads(pos_profile) + customers = get_customers_list(pos_profile) return { 'invoice': name_list, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index ed34127fbb3..fcddab40b8f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1217,18 +1217,38 @@ class SalesInvoice(SellingController): self.status = 'Draft' return + precision = self.precision("outstanding_amount") + outstanding_amount = flt(self.outstanding_amount, precision) + due_date = getdate(self.due_date) + nowdate = getdate() + + discounting_status = None + if self.is_discounted: + discountng_status = get_discounting_status(self.name) + if not status: - precision = self.precision("outstanding_amount") - args = [ - self.name, - self.outstanding_amount, - self.is_discounted, - self.is_return, - self.due_date, - self.docstatus, - precision, - ] - self.status = get_status(args) + if self.docstatus == 2: + status = "Cancelled" + elif self.docstatus == 1: + if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed': + self.status = "Overdue and Discounted" + elif outstanding_amount > 0 and due_date < nowdate: + self.status = "Overdue" + elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed': + self.status = "Unpaid and Discounted" + elif outstanding_amount > 0 and due_date >= nowdate: + self.status = "Unpaid" + #Check if outstanding amount is 0 due to credit note issued against invoice + elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): + self.status = "Credit Note Issued" + elif self.is_return == 1: + self.status = "Return" + elif outstanding_amount<=0: + self.status = "Paid" + else: + self.status = "Submitted" + else: + self.status = "Draft" if update: self.db_set('status', self.status, update_modified = update_modified) @@ -1253,42 +1273,6 @@ def get_discounting_status(sales_invoice): return status -def get_status(*args): - sales_invoice, outstanding_amount, is_discounted, is_return, due_date, docstatus, precision = args[0] - - discounting_status = None - if is_discounted: - discounting_status = get_discounting_status(sales_invoice) - - outstanding_amount = flt(outstanding_amount, precision) - due_date = getdate(due_date) - now_date = getdate() - - if docstatus == 2: - status = "Cancelled" - elif docstatus == 1: - if outstanding_amount > 0 and due_date < now_date and is_discounted and discounting_status=='Disbursed': - status = "Overdue and Discounted" - elif outstanding_amount > 0 and due_date < now_date: - status = "Overdue" - elif outstanding_amount > 0 and due_date >= now_date and is_discounted and discounting_status=='Disbursed': - status = "Unpaid and Discounted" - elif outstanding_amount > 0 and due_date >= now_date: - status = "Unpaid" - #Check if outstanding amount is 0 due to credit note issued against invoice - elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': sales_invoice, 'docstatus': 1}): - status = "Credit Note Issued" - elif is_return == 1: - status = "Return" - elif outstanding_amount <=0: - status = "Paid" - else: - status = "Submitted" - else: - status = "Draft" - - return status - def validate_inter_company_party(doctype, party, company, inter_company_reference): if not party: return diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index a2a47b3a19c..267e52a477b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -728,7 +728,7 @@ class TestSalesInvoice(unittest.TestCase): def test_make_pos_invoice(self): from erpnext.accounts.doctype.sales_invoice.pos import make_invoice - make_pos_profile() + pos_profile = make_pos_profile() pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1") pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True) @@ -744,7 +744,7 @@ class TestSalesInvoice(unittest.TestCase): pos.append("taxes", tax) invoice_data = [{'09052016142': pos}] - si = make_invoice(invoice_data).get('invoice') + si = make_invoice(pos_profile, invoice_data).get('invoice') self.assertEqual(si[0], '09052016142') sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': '09052016142', 'docstatus': 1}) @@ -762,7 +762,7 @@ class TestSalesInvoice(unittest.TestCase): if allow_negative_stock: frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0) - make_pos_profile() + pos_profile = make_pos_profile() timestamp = cint(time.time()) item = make_item("_Test POS Item") @@ -776,7 +776,7 @@ class TestSalesInvoice(unittest.TestCase): {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 330}] invoice_data = [{timestamp: pos}] - si = make_invoice(invoice_data).get('invoice') + si = make_invoice(pos_profile, invoice_data).get('invoice') self.assertEqual(si[0], timestamp) sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp}) @@ -785,7 +785,7 @@ class TestSalesInvoice(unittest.TestCase): timestamp = cint(time.time()) pos["offline_pos_name"] = timestamp invoice_data = [{timestamp: pos}] - si1 = make_invoice(invoice_data).get('invoice') + si1 = make_invoice(pos_profile, invoice_data).get('invoice') self.assertEqual(si1[0], timestamp) sales_invoice1 = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp}) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 6d53530321f..5ba455c1315 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -136,12 +136,11 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False): def make_entry(args, adv_adj, update_outstanding, from_repost=False): - args.update({"doctype": "GL Entry"}) - gle = frappe.get_doc(args) + gle = frappe.new_doc("GL Entry") + gle.update(args) gle.flags.ignore_permissions = 1 gle.flags.from_repost = from_repost gle.validate() - gle.flags.ignore_permissions = True gle.db_insert() gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost) gle.flags.ignore_validate = True diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index b465a106f0e..4d0520abc81 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -69,6 +69,17 @@ status_map = { ["Cancelled", "eval:self.docstatus==2"], ["Closed", "eval:self.status=='Closed'"], ], + "Purchase Invoice": [ + ["Draft", None], + ["Submitted", "eval:self.docstatus==1"], + ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"], + ["Return", "eval:self.is_return==1 and self.docstatus==1"], + ["Debit Note Issued", + "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], + ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], + ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], + ["Cancelled", "eval:self.docstatus==2"], + ], "Material Request": [ ["Draft", None], ["Stopped", "eval:self.status == 'Stopped'"], diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py index 524f3eb5b17..fe13d63955f 100644 --- a/erpnext/setup/doctype/company/test_company.py +++ b/erpnext/setup/doctype/company/test_company.py @@ -9,7 +9,7 @@ from frappe import _ from frappe.utils import random_string from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import get_charts_for_country -test_ignore = ["Account", "Cost Center", "Payment Terms Template", "Salary Component"] +test_ignore = ["Account", "Cost Center", "Payment Terms Template", "Salary Component", "Warehouse"] test_dependencies = ["Fiscal Year"] test_records = frappe.get_test_records('Company') diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index d2e04914017..cb0eb406df6 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -183,12 +183,17 @@ class Item(WebsiteGenerator): # default warehouse, or Stores for default in self.item_defaults or [frappe._dict({'company': frappe.defaults.get_defaults().company})]: default_warehouse = (default.default_warehouse - or frappe.db.get_single_value('Stock Settings', 'default_warehouse') - or frappe.db.get_value('Warehouse', {'warehouse_name': _('Stores')})) + or frappe.db.get_single_value('Stock Settings', 'default_warehouse')) + if default_warehouse: + warehouse_company = frappe.db.get_value("Warehouse", default_warehouse, "company") + + if not default_warehouse or warehouse_company != default.company: + default_warehouse = frappe.db.get_value('Warehouse', + {'warehouse_name': _('Stores'), 'company': default.company}) if default_warehouse: stock_entry = make_stock_entry(item_code=self.name, target=default_warehouse, qty=self.opening_stock, - rate=self.valuation_rate, company=default.company) + rate=self.valuation_rate, company=default.company) stock_entry.add_comment("Comment", _("Opening Stock")) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 7ea84e99131..6e19b71cac8 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -53,7 +53,7 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertFalse(get_gl_entries("Purchase Receipt", pr.name)) def test_batched_serial_no_purchase(self): - item = frappe.get_doc("Item", { 'item_name': 'Batched Serialized Item' }) + item = frappe.db.exists("Item", {'item_name': 'Batched Serialized Item'}) if not item: item = create_item("Batched Serialized Item") item.has_batch_no = 1 @@ -62,6 +62,8 @@ class TestPurchaseReceipt(unittest.TestCase): item.batch_number_series = "BS-BATCH-.##" item.serial_no_series = "BS-.####" item.save() + else: + item = frappe.get_doc("Item", {'item_name': 'Batched Serialized Item'}) pr = make_purchase_receipt(item_code=item.name, qty=5, rate=500) @@ -302,6 +304,8 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(serial_no, frappe.db.get_value("Serial No", {"purchase_document_type": "Purchase Receipt", "purchase_document_no": pr_doc.name}, "name")) + pr_doc.cancel() + item_code = "Test Auto Created Serial No" if not frappe.db.exists("Item", item_code): item = make_item(item_code, dict(has_serial_no=1, serial_no_series="KLJL.###")) @@ -316,9 +320,9 @@ class TestPurchaseReceipt(unittest.TestCase): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note - item_code = frappe.db.get_value('Item', {'has_serial_no': 1, 'is_fixed_asset': 0}) + item_code = frappe.db.get_value('Item', {'has_serial_no': 1, 'is_fixed_asset': 0, "has_batch_no": 0}) if not item_code: - item = make_item("Test Serial Item 1", dict(has_serial_no=1)) + item = make_item("Test Serial Item 1", dict(has_serial_no=1, has_batch_no=0)) item_code = item.name serial_no = random_string(5) From cd71114c1575756eccee1ddae9d4c160d06b9396 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Mon, 16 Mar 2020 11:14:32 +0100 Subject: [PATCH 181/929] fix(accounts): add account_type and tax_rate to some VAT accounts (#20941) * Add account_type and tax rate to some VAT accounts * fix indentation --- ..._kontenplan_SKR04_with_account_number.json | 142 ++++++++++-------- 1 file changed, 76 insertions(+), 66 deletions(-) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json index adfa9f89e23..275374a4b70 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json @@ -406,11 +406,11 @@ "is_group": 1, "Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": { "account_number": "9960" - }, - "Debitoren": { - "is_group": 1, - "account_number": "10000" - }, + }, + "Debitoren": { + "is_group": 1, + "account_number": "10000" + }, "Forderungen aus Lieferungen und Leistungen": { "account_number": "1200", "account_type": "Receivable" @@ -663,16 +663,22 @@ "account_number": "1400" }, "Abziehbare Vorsteuer 7 %": { - "account_number": "1401" + "account_number": "1401", + "account_type": "Tax", + "tax_rate": 7.0 }, "Abziehbare Vorsteuer aus innergem. Erwerb": { "account_number": "1402" }, "Abziehbare Vorsteuer aus innergem. Erwerb 19%": { - "account_number": "1404" + "account_number": "1404", + "account_type": "Tax", + "tax_rate": 19.0 }, "Abziehbare Vorsteuer 19 %": { - "account_number": "1406" + "account_number": "1406", + "account_type": "Tax", + "tax_rate": 19.0 }, "Abziehbare Vorsteuer nach \u00a7 13b UStG 19 %": { "account_number": "1407" @@ -1197,15 +1203,15 @@ "is_group": 1, "Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": { "account_number": "9964" - }, - "Kreditoren": { - "account_number": "70000", - "is_group": 1, - "Wareneingangs-­Verrechnungskonto" : { - "account_number": "70001", - "account_type": "Stock Received But Not Billed" - } - }, + }, + "Kreditoren": { + "account_number": "70000", + "is_group": 1, + "Wareneingangs-­Verrechnungskonto" : { + "account_number": "70001", + "account_type": "Stock Received But Not Billed" + } + }, "Verb. aus Lieferungen und Leistungen": { "account_number": "3300", "account_type": "Payable" @@ -1488,17 +1494,21 @@ }, "Umsatzsteuer 7 %": { "account_number": "3801", - "account_type": "Tax" + "account_type": "Tax", + "tax_rate": 7.0 }, "Umsatzsteuer aus innergem. Erwerb": { "account_number": "3802" }, "Umsatzsteuer aus innergem. Erwerb 19 %": { - "account_number": "3804" + "account_number": "3804", + "account_type": "Tax", + "tax_rate": 19.0 }, "Umsatzsteuer 19 %": { "account_number": "3806", - "account_type": "Tax" + "account_type": "Tax", + "tax_rate": 19.0 }, "Umsatzsteuer aus im Inland steuerpfl. EU-Lieferungen": { "account_number": "3807" @@ -2295,49 +2305,49 @@ }, "6 - sonstige betriebliche Ertr\u00e4ge": { "root_type": "Income", - "is_group": 1, - "Erhaltene Boni (Gruppe)": { - "is_group": 1, - "Erhaltene Boni 7 % Vorsteuer": { - "account_number": "5750" - }, - "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe": { - "account_number": "5753" - }, - "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": { - "account_number": "5754" - }, - "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": { - "account_number": "5755" - }, - "Erhaltene Boni 19 % Vorsteuer": { - "account_number": "5760" - }, - "Erhaltene Boni": { - "account_number": "5769" - } - }, - "Erhaltene Rabatte (Gruppe)": { - "is_group": 1, - "Erhaltene Rabatte": { - "account_number": "5770" - }, - "Erhaltene Rabatte 7 % Vorsteuer": { - "account_number": "5780" - }, - "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe": { - "account_number": "5783" - }, - "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": { - "account_number": "5784" - }, - "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": { - "account_number": "5785" - }, - "Erhaltene Rabatte 19 % Vorsteuer": { - "account_number": "5790" - } - }, + "is_group": 1, + "Erhaltene Boni (Gruppe)": { + "is_group": 1, + "Erhaltene Boni 7 % Vorsteuer": { + "account_number": "5750" + }, + "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe": { + "account_number": "5753" + }, + "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": { + "account_number": "5754" + }, + "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": { + "account_number": "5755" + }, + "Erhaltene Boni 19 % Vorsteuer": { + "account_number": "5760" + }, + "Erhaltene Boni": { + "account_number": "5769" + } + }, + "Erhaltene Rabatte (Gruppe)": { + "is_group": 1, + "Erhaltene Rabatte": { + "account_number": "5770" + }, + "Erhaltene Rabatte 7 % Vorsteuer": { + "account_number": "5780" + }, + "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe": { + "account_number": "5783" + }, + "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": { + "account_number": "5784" + }, + "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": { + "account_number": "5785" + }, + "Erhaltene Rabatte 19 % Vorsteuer": { + "account_number": "5790" + } + }, "Andere aktivierte Eigenleistungen": { "account_number": "4820" }, @@ -2660,8 +2670,8 @@ }, "7 - sonstige betriebliche Aufwendungen": { "root_type": "Expense", - "is_group": 1, - "Erl\u00f6sschm\u00e4lerungen (Gruppe)": { + "is_group": 1, + "Erl\u00f6sschm\u00e4lerungen (Gruppe)": { "is_group": 1, "Erl\u00f6sschm\u00e4lerungen": { "account_number": "4700" @@ -2744,7 +2754,7 @@ "Gew\u00e4hrte Rabatte 19 % USt": { "account_number": "4790" } - }, + }, "Sonstige betriebliche Aufwendungen": { "account_number": "6300" }, From 6f3941c9cc67e7d445aa6a82a52d383d56745fe3 Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Mon, 16 Mar 2020 15:49:50 +0530 Subject: [PATCH 182/929] fix: remove make_purchase_invoice from demo script (#20906) currently demo fails with the following error: frappe@bf54a9834cfe:/workspace/development/frappe-bench$ bench --site test1.localhost execute erpnext.demo.demo.make Traceback (most recent call last): File "/usr/lib/python3.7/runpy.py", line 193, in _run_module_as_main "__main__", mod_spec) File "/usr/lib/python3.7/runpy.py", line 85, in _run_code exec(code, run_globals) File "/workspace/development/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", line 97, in main() File "/workspace/development/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", line 18, in main click.Group(commands=commands)(prog_name='bench') File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 764, in __call__ return self.main(*args, **kwargs) File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 717, in main rv = self.invoke(ctx) File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 1137, in invoke return _process_result(sub_ctx.command.invoke(sub_ctx)) File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 1137, in invoke return _process_result(sub_ctx.command.invoke(sub_ctx)) File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 956, in invoke return ctx.invoke(self.callback, **ctx.params) File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 555, in invoke return callback(*args, **kwargs) File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/decorators.py", line 17, in new_func return f(get_current_context(), *args, **kwargs) File "/workspace/development/frappe-bench/apps/frappe/frappe/commands/__init__.py", line 25, in _func ret = f(frappe._dict(ctx.obj), *args, **kwargs) File "/workspace/development/frappe-bench/apps/frappe/frappe/commands/utils.py", line 145, in execute ret = frappe.get_attr(method)(*args, **kwargs) File "/workspace/development/frappe-bench/apps/frappe/frappe/__init__.py", line 1042, in get_attr return getattr(get_module(modulename), methodname) File "/workspace/development/frappe-bench/apps/frappe/frappe/__init__.py", line 823, in get_module return importlib.import_module(modulename) File "/workspace/development/frappe-bench/env/lib/python3.7/importlib/__init__.py", line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "", line 1006, in _gcd_import File "", line 983, in _find_and_load File "", line 967, in _find_and_load_unlocked File "", line 677, in _load_unlocked File "", line 728, in exec_module File "", line 219, in _call_with_frames_removed File "/workspace/development/frappe-bench/apps/erpnext/erpnext/demo/demo.py", line 6, in from erpnext.demo.user import hr, sales, purchase, manufacturing, stock, accounts, projects, fixed_asset File "/workspace/development/frappe-bench/apps/erpnext/erpnext/demo/user/fixed_asset.py", line 9, in from erpnext.assets.doctype.asset.asset import make_purchase_invoice, make_sales_invoice ImportError: cannot import name 'make_purchase_invoice' from 'erpnext.assets.doctype.asset.asset' (/workspace/development/frappe-bench/apps/erpnext/erpnext/assets/doctype/asset/asset.py) Signed-off-by: Chinmay D. Pai --- erpnext/demo/user/fixed_asset.py | 39 +++++++++----------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/erpnext/demo/user/fixed_asset.py b/erpnext/demo/user/fixed_asset.py index e6d16872024..dc094e1c918 100644 --- a/erpnext/demo/user/fixed_asset.py +++ b/erpnext/demo/user/fixed_asset.py @@ -6,46 +6,28 @@ from __future__ import unicode_literals import frappe from frappe.utils.make_random import get_random -from erpnext.assets.doctype.asset.asset import make_purchase_invoice, make_sales_invoice +from erpnext.assets.doctype.asset.asset import make_sales_invoice from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset + def work(): frappe.set_user(frappe.db.get_global('demo_accounts_user')) - asset_list = make_asset_purchase_entry() - - if not asset_list: - # fixed_asset.work() already run - return - # Enable booking asset depreciation entry automatically frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1) - + # post depreciation entries as on today post_depreciation_entries() - + # scrap a random asset frappe.db.set_value("Company", "Wind Power LLC", "disposal_account", "Gain/Loss on Asset Disposal - WPL") - + asset = get_random_asset() scrap_asset(asset.name) - - # Sell a random asset - sell_an_asset() -def make_asset_purchase_entry(): - asset_list = frappe.get_all("Asset", filters={"purchase_invoice": ["in", ("", None)]}, - fields=["name", "item_code", "gross_purchase_amount", "company", "purchase_date"]) - - # make purchase invoice - for asset in asset_list: - pi = make_purchase_invoice(asset.name, asset.item_code, asset.gross_purchase_amount, - asset.company, asset.purchase_date) - pi.supplier = get_random("Supplier") - pi.save() - pi.submit() - - return asset_list + # Sell a random asset + sell_an_asset() + def sell_an_asset(): asset = get_random_asset() @@ -55,8 +37,9 @@ def sell_an_asset(): if asset.value_after_depreciation else asset.gross_purchase_amount * 0.9 si.save() si.submit() - + + def get_random_asset(): return frappe.db.sql(""" select name, item_code, value_after_depreciation, gross_purchase_amount - from `tabAsset` + from `tabAsset` where docstatus=1 and status not in ("Scrapped", "Sold") order by rand() limit 1""", as_dict=1)[0] From 46053f6c8f572195bf665adf346d10e5c9139d69 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Mon, 16 Mar 2020 15:52:00 +0530 Subject: [PATCH 183/929] fix: fix options of currency fieldtype in Expense Taxes and Charges (#20910) --- .../expense_taxes_and_charges/expense_taxes_and_charges.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json index 9bf69daf65a..1b1889b205f 100644 --- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json +++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json @@ -70,7 +70,7 @@ "label": "Amount", "oldfieldname": "tax_amount", "oldfieldtype": "Currency", - "options": "currency" + "options": "Company:company:default_currency" }, { "columns": 2, @@ -80,7 +80,7 @@ "label": "Total", "oldfieldname": "total", "oldfieldtype": "Currency", - "options": "currency", + "options": "Company:company:default_currency", "read_only": 1 }, { From 37886a72efc4f9f9a2ffccd9fa1e396508230abd Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Mon, 16 Mar 2020 15:57:42 +0530 Subject: [PATCH 184/929] fix(HR): Leave application company field made read-only and filter in Monthly attendance sheet (#20911) * fix(HR): Leave application company field made Readonly * fix: in monthly report attandance employee filter based on company * fix: minor typo --- .../leave_application/leave_application.json | 666 +++++++++--------- .../monthly_attendance_sheet.py | 6 +- 2 files changed, 338 insertions(+), 334 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json index cdb1add3918..460be514b59 100644 --- a/erpnext/hr/doctype/leave_application/leave_application.json +++ b/erpnext/hr/doctype/leave_application/leave_application.json @@ -1,332 +1,336 @@ { - "allow_import": 1, - "autoname": "naming_series:", - "creation": "2013-02-20 11:18:11", - "description": "Apply / Approve Leaves", - "doctype": "DocType", - "document_type": "Document", - "engine": "InnoDB", - "field_order": [ - "naming_series", - "employee", - "employee_name", - "column_break_4", - "leave_type", - "department", - "leave_balance", - "section_break_5", - "from_date", - "to_date", - "half_day", - "half_day_date", - "total_leave_days", - "column_break1", - "description", - "section_break_7", - "leave_approver", - "leave_approver_name", - "column_break_18", - "status", - "salary_slip", - "sb10", - "posting_date", - "follow_via_email", - "color", - "column_break_17", - "company", - "letter_head", - "amended_from" - ], - "fields": [ - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "no_copy": 1, - "options": "HR-LAP-.YYYY.-", - "print_hide": 1, - "reqd": 1, - "set_only_once": 1 - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "in_global_search": 1, - "in_standard_filter": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "employee_name", - "fieldtype": "Data", - "in_global_search": 1, - "label": "Employee Name", - "read_only": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "fieldname": "leave_type", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "in_standard_filter": 1, - "label": "Leave Type", - "options": "Leave Type", - "reqd": 1, - "search_index": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "leave_balance", - "fieldtype": "Float", - "label": "Leave Balance Before Application", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "section_break_5", - "fieldtype": "Section Break" - }, - { - "fieldname": "from_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "From Date", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "to_date", - "fieldtype": "Date", - "label": "To Date", - "reqd": 1, - "search_index": 1 - }, - { - "default": "0", - "fieldname": "half_day", - "fieldtype": "Check", - "label": "Half Day" - }, - { - "depends_on": "eval:doc.half_day && (doc.from_date != doc.to_date)", - "fieldname": "half_day_date", - "fieldtype": "Date", - "label": "Half Day Date" - }, - { - "fieldname": "total_leave_days", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Total Leave Days", - "no_copy": 1, - "precision": "1", - "read_only": 1 - }, - { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "print_width": "50%", - "width": "50%" - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "label": "Reason" - }, - { - "fieldname": "section_break_7", - "fieldtype": "Section Break" - }, - { - "fieldname": "leave_approver", - "fieldtype": "Link", - "label": "Leave Approver", - "options": "User" - }, - { - "fieldname": "leave_approver_name", - "fieldtype": "Data", - "label": "Leave Approver Name", - "read_only": 1 - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" - }, - { - "default": "Open", - "fieldname": "status", - "fieldtype": "Select", - "in_standard_filter": 1, - "label": "Status", - "no_copy": 1, - "options": "Open\nApproved\nRejected\nCancelled" - }, - { - "fieldname": "sb10", - "fieldtype": "Section Break" - }, - { - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "label": "Posting Date", - "no_copy": 1, - "reqd": 1 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "allow_on_submit": 1, - "default": "1", - "fieldname": "follow_via_email", - "fieldtype": "Check", - "label": "Follow via Email", - "print_hide": 1 - }, - { - "fieldname": "column_break_17", - "fieldtype": "Column Break" - }, - { - "fieldname": "salary_slip", - "fieldtype": "Link", - "label": "Salary Slip", - "options": "Salary Slip", - "print_hide": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "letter_head", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Letter Head", - "options": "Letter Head", - "print_hide": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "color", - "fieldtype": "Color", - "label": "Color", - "print_hide": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Amended From", - "no_copy": 1, - "options": "Leave Application", - "print_hide": 1, - "read_only": 1 - } - ], - "icon": "fa fa-calendar", - "idx": 1, - "is_submittable": 1, - "max_attachments": 3, - "modified": "2019-08-13 13:32:04.860848", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Application", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 1, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "permlevel": 1, - "read": 1, - "role": "All" - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 1, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Leave Approver", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "permlevel": 1, - "read": 1, - "report": 1, - "role": "HR User", - "write": 1 - }, - { - "permlevel": 1, - "read": 1, - "report": 1, - "role": "Leave Approver", - "write": 1 - } - ], - "search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days", - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "employee", - "title_field": "employee_name" - } \ No newline at end of file + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-02-20 11:18:11", + "description": "Apply / Approve Leaves", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "column_break_4", + "leave_type", + "department", + "leave_balance", + "section_break_5", + "from_date", + "to_date", + "half_day", + "half_day_date", + "total_leave_days", + "column_break1", + "description", + "section_break_7", + "leave_approver", + "leave_approver_name", + "column_break_18", + "status", + "salary_slip", + "sb10", + "posting_date", + "follow_via_email", + "color", + "column_break_17", + "company", + "letter_head", + "amended_from" + ], + "fields": [ + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HR-LAP-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "employee_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "leave_type", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_standard_filter": 1, + "label": "Leave Type", + "options": "Leave Type", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "leave_balance", + "fieldtype": "Float", + "label": "Leave Balance Before Application", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "From Date", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "reqd": 1, + "search_index": 1 + }, + { + "default": "0", + "fieldname": "half_day", + "fieldtype": "Check", + "label": "Half Day" + }, + { + "depends_on": "eval:doc.half_day && (doc.from_date != doc.to_date)", + "fieldname": "half_day_date", + "fieldtype": "Date", + "label": "Half Day Date" + }, + { + "fieldname": "total_leave_days", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Total Leave Days", + "no_copy": 1, + "precision": "1", + "read_only": 1 + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "print_width": "50%", + "width": "50%" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Reason" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "leave_approver", + "fieldtype": "Link", + "label": "Leave Approver", + "options": "User" + }, + { + "fieldname": "leave_approver_name", + "fieldtype": "Data", + "label": "Leave Approver Name", + "read_only": 1 + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "options": "Open\nApproved\nRejected\nCancelled", + "permlevel": 1 + }, + { + "fieldname": "sb10", + "fieldtype": "Section Break" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "no_copy": 1, + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1, + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "allow_on_submit": 1, + "default": "1", + "fieldname": "follow_via_email", + "fieldtype": "Check", + "label": "Follow via Email", + "print_hide": 1 + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "salary_slip", + "fieldtype": "Link", + "label": "Salary Slip", + "options": "Salary Slip", + "print_hide": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "letter_head", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Letter Head", + "options": "Letter Head", + "print_hide": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "color", + "fieldtype": "Color", + "label": "Color", + "print_hide": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "options": "Leave Application", + "print_hide": 1, + "read_only": 1 + } + ], + "icon": "fa fa-calendar", + "idx": 1, + "is_submittable": 1, + "links": [], + "max_attachments": 3, + "modified": "2020-03-10 22:40:43.487721", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Application", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 1, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "role": "All" + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 1, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Leave Approver", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "report": 1, + "role": "HR User", + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "report": 1, + "role": "Leave Approver", + "write": 1 + } + ], + "search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days", + "sort_field": "modified", + "sort_order": "DESC", + "timeline_field": "employee", + "title_field": "employee_name" +} diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index 1e9c83bf3e6..aca3a175057 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -13,7 +13,7 @@ def execute(filters=None): conditions, filters = get_conditions(filters) columns = get_columns(filters) att_map = get_attendance_list(conditions, filters) - emp_map = get_employee_details() + emp_map = get_employee_details(filters) holiday_list = [emp_map[d]["holiday_list"] for d in emp_map if emp_map[d]["holiday_list"]] default_holiday_list = frappe.get_cached_value('Company', filters.get("company"), "default_holiday_list") @@ -131,10 +131,10 @@ def get_conditions(filters): return conditions, filters -def get_employee_details(): +def get_employee_details(filters): emp_map = frappe._dict() for d in frappe.db.sql("""select name, employee_name, designation, department, branch, company, - holiday_list from tabEmployee""", as_dict=1): + holiday_list from tabEmployee where company = "%s" """ % (filters.get("company")), as_dict=1): emp_map.setdefault(d.name, d) return emp_map From 43a0011987a0badcbc39c1c26f80192e7d200199 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 16 Mar 2020 16:28:54 +0530 Subject: [PATCH 185/929] fix: serial_no_selector showing for non serialized batched item (#20852) --- erpnext/stock/doctype/stock_entry/stock_entry.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index ea27c6f5d5b..92cc8f65047 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -886,10 +886,8 @@ erpnext.stock.select_batch_and_serial_no = (frm, item) => { } } - if(item && item.has_serial_no - && frm.doc.purpose === 'Material Receipt') { - return; - } + if(item && !item.has_serial_no && item.has_batch_no) return; + if (frm.doc.purpose === 'Material Receipt') return; frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() { new erpnext.SerialNoBatchSelector({ From 11fb93d022db092ea2e9ab4f6cb3f0d82f655d23 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 16 Mar 2020 16:54:34 +0530 Subject: [PATCH 186/929] fix: Add bank transaction in module view (#20823) --- erpnext/config/accounts.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index ab75f211c04..3df5fcc3ebd 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -181,6 +181,11 @@ def get_data(): "name": "Payment Reconciliation", "description": _("Match non-linked Invoices and Payments.") }, + { + "type": "doctype", + "label": _("Invoice Discounting"), + "name": "Invoice Discounting", + }, { "type": "doctype", "label": _("Update Bank Transaction Dates"), @@ -189,8 +194,9 @@ def get_data(): }, { "type": "doctype", - "label": _("Invoice Discounting"), - "name": "Invoice Discounting", + "label": _("Bank Transaction"), + "name": "Bank Transaction", + "doctype": "Bank Transaction" }, { "type": "report", From 5c760175d199a0e579073440d316773c4f0f4377 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 16 Mar 2020 17:26:02 +0530 Subject: [PATCH 187/929] fix: deletion of auto-created batch (#20953) --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index d13edc34573..c6d0c7c0a6e 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -239,7 +239,7 @@ class StockController(AccountsController): for d in self.items: if not d.batch_no: continue - serial_nos = [d.name for d in frappe.get_all("Serial No", {'batch_no': d.batch_no})] + serial_nos = [sr.name for sr in frappe.get_all("Serial No", {'batch_no': d.batch_no})] if serial_nos: frappe.db.set_value("Serial No", { 'name': ['in', serial_nos] }, "batch_no", None) From e7a6a4b009508216f9976655977abf9de1ef2ca4 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 16 Mar 2020 17:48:21 +0530 Subject: [PATCH 188/929] fix: is_pos gets reset on making s_inv from s_ord (#20887) * s_inv has is_pos default set as 1 --- erpnext/selling/doctype/sales_order/sales_order.py | 1 - erpnext/stock/doctype/delivery_note/delivery_note.py | 1 - 2 files changed, 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 4a365004da8..9e4a71d60e5 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -645,7 +645,6 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): target.set_advances() def set_missing_values(source, target): - target.is_pos = 0 target.ignore_pricing_rule = 1 target.flags.ignore_permissions = True target.run_method("set_missing_values") diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 39aad2e0071..3f74b54ce80 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -413,7 +413,6 @@ def make_sales_invoice(source_name, target_doc=None): invoiced_qty_map = get_invoiced_qty_map(source_name) def set_missing_values(source, target): - target.is_pos = 0 target.ignore_pricing_rule = 1 target.run_method("set_missing_values") target.run_method("set_po_nos") From 281a9fdf0735e9797014cfc18424d9044bc7d83e Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 16 Mar 2020 17:57:15 +0530 Subject: [PATCH 189/929] fix: handle error handling (#20814) --- erpnext/stock/doctype/delivery_trip/delivery_trip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py index 77d322ed28d..209b1ef1468 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py @@ -238,7 +238,7 @@ class DeliveryTrip(Document): try: directions = maps_client.directions(**directions_data) except Exception as e: - frappe.throw(_(e)) + frappe.throw(_(str(e))) return directions[0] if directions else False From 22a069d4b0b642fcd06ed4858731c30905930df9 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 16 Mar 2020 18:24:28 +0530 Subject: [PATCH 190/929] fix: Wrong status --- erpnext/hr/doctype/expense_claim/expense_claim.json | 4 +++- erpnext/hr/doctype/expense_claim/expense_claim.py | 2 +- erpnext/patches.txt | 1 + .../patches/v12_0/set_correct_status_for_expense_claim.py | 7 +++++++ 4 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v12_0/set_correct_status_for_expense_claim.py diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index 5c2f4901713..770b47709dd 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -278,6 +278,7 @@ "label": "More Details" }, { + "allow_on_submit": 1, "default": "Draft", "fieldname": "status", "fieldtype": "Select", @@ -366,7 +367,8 @@ "icon": "fa fa-money", "idx": 1, "is_submittable": 1, - "modified": "2019-11-08 14:13:08.964547", + "links": [], + "modified": "2020-03-16 18:11:07.861985", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim", diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index d29b3f75879..e9f15cdfb9a 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -45,7 +45,7 @@ class ExpenseClaim(AccountsController): paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount) precision = self.precision("grand_total") if (self.is_paid or (flt(self.total_sanctioned_amount) > 0 - and flt(self.grand_total, precision) == flt(paid_amount, precision))) \ + and flt(self.total_sanctioned_amount, precision) == flt(paid_amount, precision))) \ and self.docstatus == 1 and self.approval_status == 'Approved': self.status = "Paid" elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved': diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4e72d5ecd6b..ed14ecd4897 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -650,3 +650,4 @@ erpnext.patches.v12_0.update_price_or_product_discount erpnext.patches.v12_0.add_export_type_field_in_party_master erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22 erpnext.patches.v12_0.create_irs_1099_field_united_states +erpnext.patches.v12_0.set_correct_status_for_expense_claim diff --git a/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py b/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py new file mode 100644 index 00000000000..3d3980743e6 --- /dev/null +++ b/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py @@ -0,0 +1,7 @@ +# Copyright (c) 2020, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe + +def execute(): + frappe.db.sql("update `tabExpense Claim` set status = 'Paid' where (total_advance_amount + total_amount_reimbursed ) = total_sanctioned_amount") \ No newline at end of file From d2a28370347a9473628ab236ce14e8e834ae9a22 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 16 Mar 2020 21:42:51 +0530 Subject: [PATCH 191/929] fix: bom replace tool issue (#20841) Co-authored-by: Nabin Hait --- erpnext/manufacturing/doctype/bom/bom.py | 8 ++++++ .../bom_update_tool/bom_update_tool.py | 27 ++++++++++++------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 7f7ffa14bfa..b2a0b18db5e 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -487,6 +487,14 @@ class BOM(WebsiteGenerator): self.scrap_material_cost = total_sm_cost self.base_scrap_material_cost = base_total_sm_cost + def update_new_bom(self, old_bom, new_bom, rate): + for d in self.get("items"): + if d.bom_no != old_bom: continue + + d.bom_no = new_bom + d.rate = rate + d.amount = (d.stock_qty or d.qty) * rate + def update_exploded_items(self): """ Update Flat BOM, following will be correct data""" self.get_exploded_items() diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py index 31a9fdb28ab..2758a423716 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py @@ -14,10 +14,13 @@ import click class BOMUpdateTool(Document): def replace_bom(self): self.validate_bom() - self.update_new_bom() + + unit_cost = get_new_bom_unit_cost(self.new_bom) + self.update_new_bom(unit_cost) + frappe.cache().delete_key('bom_children') bom_list = self.get_parent_boms(self.new_bom) - updated_bom = [] + with click.progressbar(bom_list) as bom_list: pass for bom in bom_list: @@ -26,7 +29,9 @@ class BOMUpdateTool(Document): # this is only used for versioning and we do not want # to make separate db calls by using load_doc_before_save # which proves to be expensive while doing bulk replace - bom_obj._doc_before_save = bom_obj.as_dict() + bom_obj._doc_before_save = bom_obj + bom_obj.update_new_bom(self.current_bom, self.new_bom, unit_cost) + bom_obj.update_exploded_items() bom_obj.calculate_cost() bom_obj.update_parent_cost() bom_obj.db_update() @@ -43,14 +48,10 @@ class BOMUpdateTool(Document): != frappe.db.get_value("BOM", self.new_bom, "item"): frappe.throw(_("The selected BOMs are not for the same item")) - def update_new_bom(self): - new_bom_unitcost = frappe.db.sql("""SELECT `total_cost`/`quantity` - FROM `tabBOM` WHERE name = %s""", self.new_bom) - new_bom_unitcost = flt(new_bom_unitcost[0][0]) if new_bom_unitcost else 0 - + def update_new_bom(self, unit_cost): frappe.db.sql("""update `tabBOM Item` set bom_no=%s, rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2 and parenttype='BOM'""", - (self.new_bom, new_bom_unitcost, new_bom_unitcost, self.current_bom)) + (self.new_bom, unit_cost, unit_cost, self.current_bom)) def get_parent_boms(self, bom, bom_list=[]): data = frappe.db.sql("""SELECT DISTINCT parent FROM `tabBOM Item` @@ -65,12 +66,18 @@ class BOMUpdateTool(Document): return list(set(bom_list)) +def get_new_bom_unit_cost(bom): + new_bom_unitcost = frappe.db.sql("""SELECT `total_cost`/`quantity` + FROM `tabBOM` WHERE name = %s""", bom) + + return flt(new_bom_unitcost[0][0]) if new_bom_unitcost else 0 + @frappe.whitelist() def enqueue_replace_bom(args): if isinstance(args, string_types): args = json.loads(args) - frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.replace_bom", args=args, timeout=4000) + frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.replace_bom", args=args, timeout=40000) frappe.msgprint(_("Queued for replacing the BOM. It may take a few minutes.")) @frappe.whitelist() From 0dcd5a0f341734b7a8975c5f63bbf5144396d3a5 Mon Sep 17 00:00:00 2001 From: Don-Leopardo <46027152+Don-Leopardo@users.noreply.github.com> Date: Mon, 16 Mar 2020 14:05:01 -0300 Subject: [PATCH 192/929] fix: sql injection (#20817) --- .../doctype/bank_guarantee/bank_guarantee.py | 4 ++++ .../patient_appointment/patient_appointment.py | 4 ++-- erpnext/hr/doctype/department/department.py | 13 +++++++++---- erpnext/regional/india/utils.py | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py index 9aa2ee271a4..f28a07431fe 100644 --- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py +++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe, json from frappe.model.document import Document from frappe import _ +from frappe.desk.search import sanitize_searchfield class BankGuarantee(Document): def validate(self): @@ -22,5 +23,8 @@ class BankGuarantee(Document): @frappe.whitelist() def get_vouchar_detials(column_list, doctype, docname): + column_list = json.loads(column_list) + for col in column_list: + sanitize_searchfield(col) return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s''' .format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0] diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 7aa41c546c0..12248fcdce3 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -386,5 +386,5 @@ def get_procedure_prescribed(patient): return frappe.db.sql("""select pp.name, pp.procedure, pp.parent, ct.practitioner, ct.encounter_date, pp.practitioner, pp.date, pp.department from `tabPatient Encounter` ct, `tabProcedure Prescription` pp - where ct.patient='{0}' and pp.parent=ct.name and pp.appointment_booked=0 - order by ct.creation desc""".format(patient)) + where ct.patient=%(patient)s and pp.parent=ct.name and pp.appointment_booked=0 + order by ct.creation desc""", {"patient": patient}) diff --git a/erpnext/hr/doctype/department/department.py b/erpnext/hr/doctype/department/department.py index 9b2b5817660..2cef5092767 100644 --- a/erpnext/hr/doctype/department/department.py +++ b/erpnext/hr/doctype/department/department.py @@ -48,12 +48,17 @@ def get_abbreviated_name(name, company): @frappe.whitelist() def get_children(doctype, parent=None, company=None, is_root=False): condition = '' + var_dict = { + "name": get_root_of("Department"), + "parent": parent, + "company": company, + } if company == parent: - condition = "name='{0}'".format(get_root_of("Department")) + condition = "name=%(name)s" elif company: - condition = "parent_department='{0}' and company='{1}'".format(parent, company) + condition = "parent_department=%(parent)s and company=%(company)s" else: - condition = "parent_department = '{0}'".format(parent) + condition = "parent_department = %(parent)s" return frappe.db.sql(""" select @@ -62,7 +67,7 @@ def get_children(doctype, parent=None, company=None, is_root=False): from `tab{doctype}` where {condition} - order by name""".format(doctype=doctype, condition=condition), as_dict=1) + order by name""".format(doctype=doctype, condition=condition), var_dict, as_dict=1) @frappe.whitelist() def add_node(): diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 0f9156a6b4c..831985fc7cc 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -464,7 +464,7 @@ def get_gstins_for_company(company): `tabDynamic Link`.parent = `tabAddress`.name and `tabDynamic Link`.parenttype = 'Address' and `tabDynamic Link`.link_doctype = 'Company' and - `tabDynamic Link`.link_name = '{0}'""".format(company)) + `tabDynamic Link`.link_name = %(company)s""", {"company": company}) return company_gstins def get_address_details(data, doc, company_address, billing_address): From dd8059a3aa5ff1c6f5d7daebf5826061848200b8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Mar 2020 22:36:32 +0530 Subject: [PATCH 193/929] fix: Mode of payment not fetcched in Item wise purchase register --- .../item_wise_purchase_register.py | 7 +++++-- .../item_wise_sales_register/item_wise_sales_register.py | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 4523f66deb4..d1547f8d2cb 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -201,7 +201,8 @@ def get_columns(additional_table_columns, filters): { 'label': _('Mode Of Payment'), 'fieldname': 'mode_of_payment', - 'fieldtype': 'Data', + 'fieldtype': 'Link', + 'options': 'Mode of Payment', 'width': 120 }, { @@ -309,6 +310,8 @@ def get_items(filters, additional_query_columns): if additional_query_columns: additional_query_columns = ', ' + ', '.join(additional_query_columns) + else: + additional_query_columns = '' return frappe.db.sql(""" select @@ -320,7 +323,7 @@ def get_items(filters, additional_query_columns): `tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`, `tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`, `tabPurchase Invoice Item`.`stock_uom`, `tabPurchase Invoice Item`.`base_net_amount`, - `tabPurchase Invoice`.supplier_name, `tabPurchase Invoice`.mode_of_payment {0} + `tabPurchase Invoice`.`supplier_name`, `tabPurchase Invoice`.`mode_of_payment` {0} from `tabPurchase Invoice`, `tabPurchase Invoice Item` where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and `tabPurchase Invoice`.docstatus = 1 %s diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 786e04dd5aa..0c8957ae441 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -373,6 +373,8 @@ def get_items(filters, additional_query_columns): if additional_query_columns: additional_query_columns = ', ' + ', '.join(additional_query_columns) + else: + additional_query_columns = '' return frappe.db.sql(""" select From 7c0e89d7857baeb32ab331972101e7a5c9070bed Mon Sep 17 00:00:00 2001 From: nmimsnikhil <56826544+nmimsnikhil@users.noreply.github.com> Date: Mon, 16 Mar 2020 22:41:23 +0530 Subject: [PATCH 194/929] fix: added a Reason for Rejection field for each items (#20828) * fix: added a Reason for Rejection field for each items * Update delivery_note_item.json Co-authored-by: Nabin Hait --- .../delivery_note_item.json | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 782ac84e57d..a3386fce193 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -81,6 +81,8 @@ "accounting_dimensions_section", "cost_center", "dimension_col_break", + "reason_for_return_section_break", + "reason_for_return", "section_break_72", "page_break" ], @@ -699,12 +701,26 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "depends_on": "eval:parent.is_return==1", + "fieldname": "reason_for_return", + "fieldtype": "Small Text", + "label": "Reason for Return", + "no_copy": 1 + }, + { + "default": "0", + "depends_on": "eval:parent.is_return==1", + "fieldname": "reason_for_return_section_break", + "fieldtype": "Section Break", + "label": "Reason For Return" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-05 14:18:33.131672", + "modified": "2020-03-06 14:18:33.131672", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", @@ -712,4 +728,4 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} From b9e1d9ad7d1fec9a29f083cbc581727b44129301 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 16 Mar 2020 22:44:17 +0530 Subject: [PATCH 195/929] Deduction based on earnins/gross pay (#20935) * fix: deductions calculation based on gross pay * test: salary structure deduction based on gross pay Co-authored-by: Nabin Hait --- .../additional_salary/additional_salary.py | 6 ++- erpnext/hr/doctype/salary_slip/salary_slip.py | 48 ++++++++++--------- .../salary_structure/test_salary_structure.py | 24 +++++++++- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py index 8498b3d277d..bc7dcee55e4 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.py +++ b/erpnext/hr/doctype/additional_salary/additional_salary.py @@ -39,19 +39,21 @@ class AdditionalSalary(Document): return amount_per_day * no_of_days @frappe.whitelist() -def get_additional_salary_component(employee, start_date, end_date): +def get_additional_salary_component(employee, start_date, end_date, component_type): additional_components = frappe.db.sql(""" select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date from `tabAdditional Salary` where employee=%(employee)s and docstatus = 1 and payroll_date between %(from_date)s and %(to_date)s + and type = %(component_type)s group by salary_component, overwrite_salary_structure_amount order by salary_component, overwrite_salary_structure_amount """, { 'employee': employee, 'from_date': start_date, - 'to_date': end_date + 'to_date': end_date, + 'component_type': "Earning" if component_type == "earnings" else "Deduction" }, as_dict=1) additional_components_list = [] diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 0171643b13c..e126db5f5b1 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -298,9 +298,11 @@ class SalarySlip(TransactionBase): def calculate_net_pay(self): if self.salary_structure: - self.calculate_component_amounts() - + self.calculate_component_amounts("earnings") self.gross_pay = self.get_component_totals("earnings") + + if self.salary_structure: + self.calculate_component_amounts("deductions") self.total_deduction = self.get_component_totals("deductions") self.set_loan_repayment() @@ -308,25 +310,27 @@ class SalarySlip(TransactionBase): self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment)) self.rounded_total = rounded(self.net_pay) - def calculate_component_amounts(self): + def calculate_component_amounts(self, component_type): if not getattr(self, '_salary_structure_doc', None): self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure) payroll_period = get_payroll_period(self.start_date, self.end_date, self.company) - self.add_structure_components() - self.add_employee_benefits(payroll_period) - self.add_additional_salary_components() - self.add_tax_components(payroll_period) - self.set_component_amounts_based_on_payment_days() + self.add_structure_components(component_type) + self.add_additional_salary_components(component_type) + if component_type == "earnings": + self.add_employee_benefits(payroll_period) + else: + self.add_tax_components(payroll_period) - def add_structure_components(self): + self.set_component_amounts_based_on_payment_days(component_type) + + def add_structure_components(self, component_type): data = self.get_data_for_eval() - for key in ('earnings', 'deductions'): - for struct_row in self._salary_structure_doc.get(key): - amount = self.eval_condition_and_formula(struct_row, data) - if amount and struct_row.statistical_component == 0: - self.update_component_row(struct_row, amount, key) + for struct_row in self._salary_structure_doc.get(component_type): + amount = self.eval_condition_and_formula(struct_row, data) + if amount and struct_row.statistical_component == 0: + self.update_component_row(struct_row, amount, component_type) def get_data_for_eval(self): '''Returns data for evaluating formula''' @@ -399,14 +403,15 @@ class SalarySlip(TransactionBase): amount = last_benefit.amount self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings") - def add_additional_salary_components(self): - additional_components = get_additional_salary_component(self.employee, self.start_date, self.end_date) + def add_additional_salary_components(self, component_type): + additional_components = get_additional_salary_component(self.employee, + self.start_date, self.end_date, component_type) if additional_components: for additional_component in additional_components: amount = additional_component.amount overwrite = additional_component.overwrite - key = "earnings" if additional_component.type == "Earning" else "deductions" - self.update_component_row(frappe._dict(additional_component.struct_row), amount, key, overwrite=overwrite) + self.update_component_row(frappe._dict(additional_component.struct_row), amount, + component_type, overwrite=overwrite) def add_tax_components(self, payroll_period): # Calculate variable_based_on_taxable_salary after all components updated in salary slip @@ -735,7 +740,7 @@ class SalarySlip(TransactionBase): total += d.amount return total - def set_component_amounts_based_on_payment_days(self): + def set_component_amounts_based_on_payment_days(self, component_type): joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee, ["date_of_joining", "relieving_date"]) @@ -745,9 +750,8 @@ class SalarySlip(TransactionBase): if not joining_date: frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name))) - for component_type in ("earnings", "deductions"): - for d in self.get(component_type): - d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0] + for d in self.get(component_type): + d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0] def set_loan_repayment(self): self.set('loans', []) diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index 848c3df57ab..f67a189ee71 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -25,7 +25,6 @@ class TestSalaryStructure(unittest.TestCase): make_employee("test_employee@salary.com") make_employee("test_employee_2@salary.com") - def make_holiday_list(self): if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"): holiday_list = frappe.get_doc({ @@ -38,6 +37,29 @@ class TestSalaryStructure(unittest.TestCase): holiday_list.get_weekly_off_dates() holiday_list.save() + def test_salary_structure_deduction_based_on_gross_pay(self): + + emp = make_employee("test_employee_3@salary.com") + + sal_struct = make_salary_structure("Salary Structure 2", "Monthly", dont_submit = True) + + sal_struct.earnings = [sal_struct.earnings[0]] + sal_struct.earnings[0].amount_based_on_formula = 1 + sal_struct.earnings[0].formula = "base" + + sal_struct.deductions = [sal_struct.deductions[0]] + + sal_struct.deductions[0].amount_based_on_formula = 1 + sal_struct.deductions[0].condition = "gross_pay > 100" + sal_struct.deductions[0].formula = "gross_pay * 0.2" + + sal_struct.submit() + + assignment = create_salary_structure_assignment(emp, "Salary Structure 2") + ss = make_salary_slip(sal_struct.name, employee = emp) + + self.assertEqual(assignment.base * 0.2, ss.deductions[0].amount) + def test_amount_totals(self): frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"}) From 756f47af04d57fd402f5f0874e04a850f28c4850 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 17 Mar 2020 10:52:56 +0530 Subject: [PATCH 196/929] feat: Nested set filtering for accounting dimension (#20866) * feat: Nested set filtering for accounting dimension * fix: Remove print statement --- .../accounting_dimension/accounting_dimension.py | 14 +++++++++++++- .../accounts_receivable/accounts_receivable.py | 14 ++++++++------ erpnext/accounts/report/financial_statements.py | 13 ++++++++----- .../report/general_ledger/general_ledger.py | 11 +++++++---- .../report/sales_register/sales_register.py | 12 ++++++++---- .../accounts/report/trial_balance/trial_balance.py | 13 ++++++++----- 6 files changed, 52 insertions(+), 25 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 522ed4ffa46..462d967d015 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -172,7 +172,7 @@ def get_doctypes_with_dimensions(): return doclist def get_accounting_dimensions(as_list=True): - accounting_dimensions = frappe.get_all("Accounting Dimension", fields=["label", "fieldname", "disabled"]) + accounting_dimensions = frappe.get_all("Accounting Dimension", fields=["label", "fieldname", "disabled", "document_type"]) if as_list: return [d.fieldname for d in accounting_dimensions] @@ -186,6 +186,18 @@ def get_checks_for_pl_and_bs_accounts(): return dimensions +def get_dimension_with_children(doctype, dimension): + + if isinstance(dimension, list): + dimension = dimension[0] + + all_dimensions = [] + lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"]) + children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}) + all_dimensions += [c.name for c in children] + + return all_dimensions + @frappe.whitelist() def get_dimension_filters(): dimension_filters = frappe.db.sql(""" diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 0438f6d09e1..240b0d83817 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -7,7 +7,7 @@ from frappe import _, scrub from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr, now, time_diff_in_seconds from collections import OrderedDict from erpnext.accounts.utils import get_currency_precision -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children # This report gives a summary of all Outstanding Invoices considering the following @@ -603,7 +603,6 @@ class ReceivablePayableReport(object): self.add_supplier_filters(conditions, values) self.add_accounting_dimensions_filters(conditions, values) - return " and ".join(conditions), values def get_order_by_condition(self): @@ -666,13 +665,16 @@ class ReceivablePayableReport(object): doctype=doctype, lft=lft, rgt=rgt, key=key) def add_accounting_dimensions_filters(self, conditions, values): - accounting_dimensions = get_accounting_dimensions() + accounting_dimensions = get_accounting_dimensions(as_list=False) 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)) + if self.filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + self.filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + self.filters.get(dimension.fieldname)) + conditions.append("{0} in %s".format(dimension.fieldname)) + values.append(tuple(self.filters.get(dimension.fieldname))) def get_gle_balance(self, gle): # get the balance of the GL (debit - credit) or reverse balance based on report type diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 75433a135f3..2636a1d2efc 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -16,7 +16,7 @@ from frappe import _ from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate, cstr) from six import itervalues -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False, company=None, reset_period_on_fy_change=True): @@ -389,7 +389,7 @@ def set_gl_entries_by_account( def get_additional_conditions(from_date, ignore_closing_entries, filters): additional_conditions = [] - accounting_dimensions = get_accounting_dimensions() + accounting_dimensions = get_accounting_dimensions(as_list=False) if ignore_closing_entries: additional_conditions.append("ifnull(voucher_type, '')!='Period Closing Voucher'") @@ -412,11 +412,14 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") else: additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") - + if accounting_dimensions: for dimension in accounting_dimensions: - if filters.get(dimension): - additional_conditions.append("{0} in (%({0})s)".format(dimension)) + if filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + filters.get(dimension.fieldname)) + additional_conditions.append("{0} in %({0})s".format(dimension.fieldname)) return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else "" diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 0939354efa7..f45911f90e5 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -10,7 +10,7 @@ from frappe import _, _dict 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 erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children from collections import OrderedDict def execute(filters=None): @@ -194,12 +194,15 @@ def get_conditions(filters): if match_conditions: conditions.append(match_conditions) - accounting_dimensions = get_accounting_dimensions() + accounting_dimensions = get_accounting_dimensions(as_list=False) if accounting_dimensions: for dimension in accounting_dimensions: - if filters.get(dimension): - conditions.append("{0} in (%({0})s)".format(dimension)) + if filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + filters.get(dimension.fieldname)) + conditions.append("{0} in %({0})s".format(dimension.fieldname)) return "and {}".format(" and ".join(conditions)) if conditions else "" diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index 2be90bc5264..9864e40c1a9 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -6,7 +6,7 @@ import frappe from frappe.utils import flt from frappe import msgprint, _ from frappe.model.meta import get_field_precision -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children def execute(filters=None): return _execute(filters) @@ -341,14 +341,18 @@ def get_conditions(filters): where parent=`tabSales Invoice`.name and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)""" - accounting_dimensions = get_accounting_dimensions() + accounting_dimensions = get_accounting_dimensions(as_list=False) if accounting_dimensions: for dimension in accounting_dimensions: - if filters.get(dimension): + if filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + filters.get(dimension.fieldname)) + conditions += """ and exists(select name from `tabSales Invoice Item` where parent=`tabSales Invoice`.name - and ifnull(`tabSales Invoice Item`.{0}, '') = %({0})s)""".format(dimension) + and ifnull(`tabSales Invoice Item`.{0}, '') in %({0})s)""".format(dimension.fieldname) return conditions diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 69285cc9634..5fe6b416fda 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -7,7 +7,7 @@ from frappe import _ from frappe.utils import flt, getdate, formatdate, cstr from erpnext.accounts.report.financial_statements \ import filter_accounts, set_gl_entries_by_account, filter_out_zero_value_rows -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children value_fields = ("opening_debit", "opening_credit", "debit", "credit", "closing_debit", "closing_credit") @@ -109,7 +109,7 @@ def get_rootwise_opening_balances(filters, report_type): additional_conditions += fb_conditions - accounting_dimensions = get_accounting_dimensions() + accounting_dimensions = get_accounting_dimensions(as_list=False) query_filters = { "company": filters.company, @@ -122,11 +122,14 @@ def get_rootwise_opening_balances(filters, report_type): if accounting_dimensions: for dimension in accounting_dimensions: - if filters.get(dimension): - additional_conditions += """ and {0} in (%({0})s) """.format(dimension) + if filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + filters.get(dimension.fieldname)) + additional_conditions += "and {0} in %({0})s".format(dimension.fieldname) query_filters.update({ - dimension: filters.get(dimension) + dimension.fieldname: filters.get(dimension.fieldname) }) gle = frappe.db.sql(""" From 3ddcc2e5c77cda7cf82125e46d6fd0f54c58d26a Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 17 Mar 2020 10:56:13 +0530 Subject: [PATCH 197/929] fix: Item alternative must have similar fields as orignal item (#20799) * fix: Item alternative must have similar fields as orignal item * fix: Using db.get_values and meta instead of get_value and get_doc * fix: Made code more DRY Co-authored-by: Nabin Hait --- .../doctype/item_alternative/item_alternative.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/erpnext/stock/doctype/item_alternative/item_alternative.py b/erpnext/stock/doctype/item_alternative/item_alternative.py index 8e54539b18b..0c0d5b3271f 100644 --- a/erpnext/stock/doctype/item_alternative/item_alternative.py +++ b/erpnext/stock/doctype/item_alternative/item_alternative.py @@ -22,6 +22,21 @@ class ItemAlternative(Document): if self.item_code == self.alternative_item_code: frappe.throw(_("Alternative item must not be same as item code")) + item_meta = frappe.get_meta("Item") + fields = ["is_stock_item", "include_item_in_manufacturing","has_serial_no","has_batch_no"] + item_data = frappe.db.get_values("Item", self.item_code, fields, as_dict=1) + alternative_item_data = frappe.db.get_values("Item", self.alternative_item_code, fields, as_dict=1) + + for field in fields: + if item_data[0].get(field) != alternative_item_data[0].get(field): + raise_exception, alert = [1, False] if field == "is_stock_item" else [0, True] + + frappe.msgprint(_("The value of {0} differs between Items {1} and {2}") \ + .format(frappe.bold(item_meta.get_label(field)), + frappe.bold(self.alternative_item_code), + frappe.bold(self.item_code)), + alert=alert, raise_exception=raise_exception) + def validate_duplicate(self): if frappe.db.get_value("Item Alternative", {'item_code': self.item_code, 'alternative_item_code': self.alternative_item_code, 'name': ('!=', self.name)}): From 7f58f6af3997fcd6d52460d107cb9082e70d3cba Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 17 Mar 2020 10:58:17 +0530 Subject: [PATCH 198/929] fix: leave based on multiple holiday list (#20849) Co-authored-by: Nabin Hait --- .../leave_application/leave_application.py | 20 +++++++++++-------- .../leave_ledger_entry.json | 11 +++++++++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 8c109464fdf..39e5e1132ca 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -366,7 +366,8 @@ class LeaveApplication(Document): leaves=self.total_leave_days * -1, from_date=self.from_date, to_date=self.to_date, - is_lwp=lwp + is_lwp=lwp, + holiday_list=get_holiday_list_for_employee(self.employee) ) create_leave_ledger_entry(self, args, submit) @@ -376,7 +377,9 @@ class LeaveApplication(Document): from_date=self.from_date, to_date=expiry_date, leaves=(date_diff(expiry_date, self.from_date) + 1) * -1, - is_lwp=lwp + is_lwp=lwp, + holiday_list=get_holiday_list_for_employee(self.employee), + ) create_leave_ledger_entry(self, args, submit) @@ -402,7 +405,7 @@ def get_allocation_expiry(employee, leave_type, to_date, from_date): return expiry[0]['to_date'] if expiry else None @frappe.whitelist() -def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None): +def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None, holiday_list = None): number_of_days = 0 if cint(half_day) == 1: if from_date == to_date: @@ -416,7 +419,7 @@ def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day number_of_days = date_diff(to_date, from_date) + 1 if not frappe.db.get_value("Leave Type", leave_type, "include_holiday"): - number_of_days = flt(number_of_days) - flt(get_holidays(employee, from_date, to_date)) + number_of_days = flt(number_of_days) - flt(get_holidays(employee, from_date, to_date, holiday_list=holiday_list)) return number_of_days @frappe.whitelist() @@ -567,7 +570,7 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date): {'name': leave_entry.transaction_name}, ['half_day_date']) leave_days += get_number_of_leave_days(employee, leave_type, - leave_entry.from_date, leave_entry.to_date, half_day, half_day_date) * -1 + leave_entry.from_date, leave_entry.to_date, half_day, half_day_date, holiday_list=leave_entry.holiday_list) * -1 return leave_days @@ -580,7 +583,7 @@ def get_leave_entries(employee, leave_type, from_date, to_date): ''' Returns leave entries between from_date and to_date ''' return frappe.db.sql(""" SELECT - employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, + employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, holiday_list, is_carry_forward, is_expired FROM `tabLeave Ledger Entry` WHERE employee=%(employee)s AND leave_type=%(leave_type)s @@ -596,9 +599,10 @@ def get_leave_entries(employee, leave_type, from_date, to_date): }, as_dict=1) @frappe.whitelist() -def get_holidays(employee, from_date, to_date): +def get_holidays(employee, from_date, to_date, holiday_list = None): '''get holidays between two dates for the given employee''' - holiday_list = get_holiday_list_for_employee(employee) + if not holiday_list: + holiday_list = get_holiday_list_for_employee(employee) holidays = frappe.db.sql("""select count(distinct holiday_date) from `tabHoliday` h1, `tabHoliday List` h2 where h1.parent = h2.name and h1.holiday_date between %s and %s diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json index 771e706bbb3..a5ac3f3d471 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-05-09 15:47:39.760406", "doctype": "DocType", "engine": "InnoDB", @@ -12,6 +13,7 @@ "column_break_7", "from_date", "to_date", + "holiday_list", "is_carry_forward", "is_expired", "is_lwp", @@ -98,11 +100,18 @@ "fieldname": "is_lwp", "fieldtype": "Check", "label": "Is Leave Without Pay" + }, + { + "fieldname": "holiday_list", + "fieldtype": "Link", + "label": "Holiday List", + "options": "Holiday List" } ], "in_create": 1, "is_submittable": 1, - "modified": "2019-08-20 14:40:04.130799", + "links": [], + "modified": "2020-02-27 14:40:10.502605", "modified_by": "Administrator", "module": "HR", "name": "Leave Ledger Entry", From 6c148ef314b6b89983f3f16efd6af912c64abfc6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 17 Mar 2020 11:43:34 +0530 Subject: [PATCH 199/929] fix: Add check to skip SO and DN in customer master --- .../doctype/sales_invoice/sales_invoice.py | 18 +++++++++++------- erpnext/selling/doctype/customer/customer.json | 16 +++++++++++++++- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index d0fe4f4832a..9c87f1e82d4 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -412,7 +412,7 @@ class SalesInvoice(SellingController): if pos: self.allow_print_before_pay = pos.allow_print_before_pay - + if not for_validate: self.tax_category = pos.get("tax_category") @@ -534,14 +534,18 @@ class SalesInvoice(SellingController): """check in manage account if sales order / delivery note required or not.""" if self.is_return: return - dic = {'Sales Order':['so_required', 'is_pos'],'Delivery Note':['dn_required', 'update_stock']} - for i in dic: - if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes': + + prev_doc_field_map = {'Sales Order': ['so_required', 'is_pos'],'Delivery Note': ['dn_required', 'update_stock']} + for key, value in iteritems(prev_doc_field_map): + if frappe.db.get_single_value('Selling Settings', value[0]) == 'Yes': + + if frappe.get_value('Customer', self.customer, value[0]): + continue + for d in self.get('items'): is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item') - if (d.item_code and is_stock_item == 1\ - and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])): - msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1) + if (d.item_code and is_stock_item ==1 and not d.get(key.lower().replace(' ', '_')) and not self.get(value[1])): + msgprint(_("{0} is mandatory for Item {1}").format(key, d.item_code), raise_exception=1) def validate_proj_cust(self): diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 89ce325a847..a93ab50639d 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -25,6 +25,8 @@ "territory", "tax_id", "tax_category", + "so_required", + "dn_required", "disabled", "is_internal_customer", "represents_company", @@ -466,13 +468,25 @@ "fieldtype": "Table", "label": "Credit Limit", "options": "Customer Credit Limit" + }, + { + "default": "0", + "fieldname": "so_required", + "fieldtype": "Check", + "label": "Allow Sales Invoice Creation Without Sales Order" + }, + { + "default": "0", + "fieldname": "dn_required", + "fieldtype": "Check", + "label": "Allow Sales Invoice Creation Without Delivery Note" } ], "icon": "fa fa-user", "idx": 363, "image_field": "image", "links": [], - "modified": "2020-01-29 20:36:37.879581", + "modified": "2020-03-17 11:03:42.706907", "modified_by": "Administrator", "module": "Selling", "name": "Customer", From 3528149329f610f8c26e64d84ee3449e5efc724f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 17 Mar 2020 11:44:02 +0530 Subject: [PATCH 200/929] fix: Add check to skip PR in supplier master --- .../doctype/purchase_invoice/purchase_invoice.py | 8 ++++++-- erpnext/buying/doctype/supplier/supplier.json | 11 +++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 19edd060a8e..d8e0f21155a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -263,17 +263,21 @@ class PurchaseInvoice(BuyingController): if not d.purchase_order: throw(_("""Purchase Order Required for item {0} To submit the invoice without purchase order please set - {1} as {2} in {3}""").format(frappe.bold(d.item_code), frappe.bold('Purchase Order Required'), + {1} as {2} in {3}""").format(frappe.bold(d.item_code), frappe.bold(_('Purchase Order Required')), frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))) def pr_required(self): stock_items = self.get_stock_items() if frappe.db.get_value("Buying Settings", None, "pr_required") == 'Yes': + + if frappe.get_value('Supplier', self.supplier, 'allow_purchase_invoice_creation_without_purchase_receipt'): + return + for d in self.get('items'): if not d.purchase_receipt and d.item_code in stock_items: throw(_("""Purchase Receipt Required for item {0} To submit the invoice without purchase receipt please set - {1} as {2} in {3}""").format(frappe.bold(d.item_code), frappe.bold('Purchase Receipt Required'), + {1} as {2} in {3}""").format(frappe.bold(d.item_code), frappe.bold(_('Purchase Receipt Required')), frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))) def validate_write_off_account(self): diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 6ad238d2b38..e78abd62615 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -28,6 +28,7 @@ "pan", "language", "allow_purchase_invoice_creation_without_purchase_order", + "allow_purchase_invoice_creation_without_purchase_receipt", "disabled", "warn_rfqs", "warn_pos", @@ -371,14 +372,20 @@ "default": "0", "fieldname": "allow_purchase_invoice_creation_without_purchase_order", "fieldtype": "Check", - "label": "Allow Purchase Invoice creation without Purchase Order" + "label": "Allow Purchase Invoice Creation Without Purchase Order" + }, + { + "default": "0", + "fieldname": "allow_purchase_invoice_creation_without_purchase_receipt", + "fieldtype": "Check", + "label": "Allow Purchase Invoice Creation Without Purchase Receipt" } ], "icon": "fa fa-user", "idx": 370, "image_field": "image", "links": [], - "modified": "2020-03-06 19:04:06.097092", + "modified": "2020-03-17 09:48:30.578242", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", From 6f454dce20fe1b78e106e6cdc8680dba732afbc7 Mon Sep 17 00:00:00 2001 From: Sun Howwrongbum Date: Tue, 17 Mar 2020 12:13:01 +0530 Subject: [PATCH 201/929] fix: null fields being set in Integration Request (#20894) Co-authored-by: Nabin Hait --- .../accounts/doctype/payment_request/payment_request.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 1aff43c7177..6b74f477f2b 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -126,12 +126,12 @@ class PaymentRequest(Document): return controller.get_payment_url(**{ "amount": flt(self.grand_total, self.precision("grand_total")), - "title": data.company.encode("utf-8"), - "description": self.subject.encode("utf-8"), + "title": frappe.as_unicode(data.company), + "description": frappe.as_unicode(self.subject), "reference_doctype": "Payment Request", "reference_docname": self.name, "payer_email": self.email_to or frappe.session.user, - "payer_name": frappe.safe_encode(data.customer_name), + "payer_name": frappe.as_unicode(data.customer_name), "order_id": self.name, "currency": self.currency }) @@ -475,4 +475,4 @@ def make_payment_order(source_name, target_doc=None): } }, target_doc, set_missing_values) - return doclist \ No newline at end of file + return doclist From 224059e7bac7479f41432e1b30a5326687c42360 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 17 Mar 2020 16:59:09 +0530 Subject: [PATCH 202/929] fix: Party Name not visible in statement of accounts (#20914) * fix: Party name in Statement Of Accounts * Update general_ledger.html Co-authored-by: Nabin Hait --- erpnext/accounts/report/general_ledger/general_ledger.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 40469aecc1d..9a2205a5791 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -2,7 +2,7 @@

{% if (filters.party_name) { %} {%= filters.party_name %} - {% } else if (filters.party && filters.show_name) { %} + {% } else if (filters.party) { %} {%= filters.party %} {% } else if (filters.account) { %} {%= filters.account %} From bc8a881e649e9ee7d0001e061e04ea59e86e6507 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 17 Mar 2020 17:00:24 +0530 Subject: [PATCH 203/929] fix: purchase return are allowed even when assets are not cancelled (#20798) * fix: purchase return are allowed even when assets are not cancelled * chore: test case * fix: error message Co-authored-by: Nabin Hait --- erpnext/controllers/buying_controller.py | 14 ++++++++++ .../purchase_receipt/test_purchase_receipt.py | 27 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 75b896bb134..a47507eaf7b 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -44,6 +44,7 @@ class BuyingController(StockController): self.validate_stock_or_nonstock_items() self.validate_warehouse() self.set_supplier_address() + self.validate_asset_return() if self.doctype=="Purchase Invoice": self.validate_purchase_receipt_if_update_stock() @@ -99,6 +100,19 @@ class BuyingController(StockController): for d in tax_for_valuation: d.category = 'Total' msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items')) + + def validate_asset_return(self): + if self.doctype not in ['Purchase Receipt', 'Purchase Invoice'] or not self.is_return: + return + + purchase_doc_field = 'purchase_receipt' if self.doctype == 'Purchase Receipt' else 'purchase_invoice' + not_cancelled_asset = [d.name for d in frappe.db.get_all("Asset", { + purchase_doc_field: self.return_against, + "docstatus": 1 + })] + if self.is_return and len(not_cancelled_asset): + frappe.throw(_("{} has submitted assets linked to it. You need to cancel the assets to create purchase return.".format(self.return_against)), + title=_("Not Allowed")) def get_asset_items(self): if self.doctype not in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']: diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 6e19b71cac8..a51b25bf36d 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -374,6 +374,33 @@ class TestPurchaseReceipt(unittest.TestCase): location = frappe.db.get_value('Asset', assets[0].name, 'location') self.assertEquals(location, "Test Location") + + def test_purchase_return_with_submitted_asset(self): + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return + + pr = make_purchase_receipt(item_code="Test Asset Item", qty=1) + + asset = frappe.get_doc("Asset", { + 'purchase_receipt': pr.name + }) + asset.available_for_use_date = frappe.utils.nowdate() + asset.gross_purchase_amount = 50.0 + asset.append("finance_books", { + "expected_value_after_useful_life": 10, + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 1, + "depreciation_start_date": frappe.utils.nowdate() + }) + asset.submit() + + pr_return = make_purchase_return(pr.name) + self.assertRaises(frappe.exceptions.ValidationError, pr_return.submit) + + asset.load_from_db() + asset.cancel() + + pr_return.submit() def test_purchase_receipt_for_enable_allow_cost_center_in_entry_of_bs_account(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center From a6cd666b10a41566e450433be3ffac9abd0643b9 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 17 Mar 2020 17:00:41 +0530 Subject: [PATCH 204/929] fix: reserved qty for production issue for partial completion of work order (#20900) --- .../doctype/work_order/test_work_order.py | 34 ++++++++++++++++++- .../doctype/work_order/work_order.py | 6 ++-- erpnext/stock/doctype/bin/bin.py | 14 +++++--- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index ea2e7a96e1d..f917b098688 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -14,6 +14,7 @@ from erpnext.stock.utils import get_bin from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.stock.doctype.item.test_item import make_item from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom +from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse class TestWorkOrder(unittest.TestCase): def setUp(self): @@ -82,6 +83,37 @@ class TestWorkOrder(unittest.TestCase): wo_order.set_work_order_operations() self.assertEqual(wo_order.planned_operating_cost, cost*2) + def test_resered_qty_for_partial_completion(self): + item = "_Test Item" + warehouse = create_warehouse("Test Warehouse for reserved_qty - _TC") + + bin1_at_start = get_bin(item, warehouse) + + # reset to correct value + bin1_at_start.update_reserved_qty_for_production() + + wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2, + source_warehouse=warehouse, skip_transfer=1) + + bin1_on_submit = get_bin(item, warehouse) + + # reserved qty for production is updated + self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2, + cint(bin1_on_submit.reserved_qty_for_production)) + + test_stock_entry.make_stock_entry(item_code="_Test Item", + target=warehouse, qty=100, basic_rate=100) + test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100", + target=warehouse, qty=100, basic_rate=100) + + s = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 1)) + s.submit() + + bin1_at_completion = get_bin(item, warehouse) + + self.assertEqual(cint(bin1_at_completion.reserved_qty_for_production), + cint(bin1_on_submit.reserved_qty_for_production) - 1) + def test_production_item(self): wo_order = make_wo_order_test_record(item="_Test FG Item", qty=1, do_not_save=True) frappe.db.set_value("Item", "_Test FG Item", "end_of_life", "2000-1-1") @@ -368,7 +400,7 @@ def make_wo_order_test_record(**args): wo_order.company = args.company or "_Test Company" wo_order.stock_uom = args.stock_uom or "_Test UOM" wo_order.use_multi_level_bom=0 - wo_order.skip_transfer=1 + wo_order.skip_transfer=args.skip_transfer or 0 wo_order.get_items_and_operations_from_bom() wo_order.sales_order = args.sales_order or None diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index b4dc3a55699..28bcc2e0f9c 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -429,6 +429,9 @@ class WorkOrder(Document): update bin reserved_qty_for_production called from Stock Entry for production, after submit, cancel ''' + # calculate consumed qty based on submitted stock entries + self.update_consumed_qty_for_required_items() + if self.docstatus==1: # calculate transferred qty based on submitted stock entries self.update_transaferred_qty_for_required_items() @@ -436,9 +439,6 @@ class WorkOrder(Document): # update in bin self.update_reserved_qty_for_production() - # calculate consumed qty based on submitted stock entries - self.update_consumed_qty_for_required_items() - def update_reserved_qty_for_production(self, items=None): '''update reserved_qty_for_production in bins''' for d in self.required_items: diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 97a84726da8..73b36e3d852 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -69,15 +69,21 @@ class Bin(Document): '''Update qty reserved for production from Production Item tables in open work orders''' self.reserved_qty_for_production = frappe.db.sql(''' - select sum(item.required_qty - item.transferred_qty) - from `tabWork Order` pro, `tabWork Order Item` item - where + SELECT + CASE WHEN ifnull(skip_transfer, 0) = 0 THEN + SUM(item.required_qty - item.transferred_qty) + ELSE + SUM(item.required_qty - item.consumed_qty) + END + FROM `tabWork Order` pro, `tabWork Order Item` item + WHERE item.item_code = %s and item.parent = pro.name and pro.docstatus = 1 and item.source_warehouse = %s and pro.status not in ("Stopped", "Completed") - and item.required_qty > item.transferred_qty''', (self.item_code, self.warehouse))[0][0] + and (item.required_qty > item.transferred_qty or item.required_qty > item.consumed_qty) + ''', (self.item_code, self.warehouse))[0][0] self.set_projected_qty() From 738517c6e81218e89a4a00ade5cc0d6551d7658e Mon Sep 17 00:00:00 2001 From: Priyanka Gangar <59438065+0925-pinka@users.noreply.github.com> Date: Tue, 17 Mar 2020 19:39:06 +0530 Subject: [PATCH 205/929] Label changes in account settings hotfix (#20964) * fix: Account setting words switches to US Terminology * created united_states.js * Remove Unwanted Spaces * Remove all changes * Add spaces between arguments Co-authored-by: PriyankaGangar --- .../doctype/accounts_settings/regional/united_states.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 erpnext/accounts/doctype/accounts_settings/regional/united_states.js diff --git a/erpnext/accounts/doctype/accounts_settings/regional/united_states.js b/erpnext/accounts/doctype/accounts_settings/regional/united_states.js new file mode 100644 index 00000000000..d47d6e58039 --- /dev/null +++ b/erpnext/accounts/doctype/accounts_settings/regional/united_states.js @@ -0,0 +1,8 @@ + +frappe.ui.form.on('Accounts Settings', { + refresh: function(frm) { + frm.set_df_property("acc_frozen_upto", "label", "Books Closed Through"); + frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods"); + frm.set_df_property("credit_controller", "label", "Credit Manager"); + } +}); \ No newline at end of file From 7cf245895e84604ec141a3d5c88455c515c345b7 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 2 Dec 2019 17:08:12 +0530 Subject: [PATCH 206/929] refactor: Report BOM Sock Calculated (#19431) --- .../bom_stock_calculated.py | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py index be016ad9a47..f7b407b7922 100644 --- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py +++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils.data import comma_and def execute(filters=None): # if not filters: filters = {} @@ -13,35 +14,36 @@ def execute(filters=None): data = get_bom_stock(filters) qty_to_make = filters.get("qty_to_make") + manufacture_details = get_manufacturer_records() for row in data: - item_map = get_item_details(row.item_code) reqd_qty = qty_to_make * row.actual_qty last_pur_price = frappe.db.get_value("Item", row.item_code, "last_purchase_rate") - if row.to_build > 0: - diff_qty = row.to_build - reqd_qty - summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, row.to_build, reqd_qty, diff_qty, last_pur_price]) - else: - diff_qty = 0 - reqd_qty - summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, "0.000", reqd_qty, diff_qty, last_pur_price]) + summ_data.append(get_report_data(last_pur_price, reqd_qty, row, manufacture_details)) return columns, summ_data +def get_report_data(last_pur_price, reqd_qty, row, manufacture_details): + to_build = row.to_build if row.to_build > 0 else 0 + diff_qty = to_build - reqd_qty + return [row.item_code, row.description, + comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer', []), add_quotes=False), + comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer_part', []), add_quotes=False), + row.actual_qty, str(to_build), + reqd_qty, diff_qty, last_pur_price] + def get_columns(): """return columns""" columns = [ _("Item") + ":Link/Item:100", _("Description") + "::150", - _("Manufacturer") + "::100", - _("Manufacturer Part Number") + "::100", + _("Manufacturer") + "::250", + _("Manufacturer Part Number") + "::250", _("Qty") + ":Float:50", _("Stock Qty") + ":Float:100", _("Reqd Qty")+ ":Float:100", _("Diff Qty")+ ":Float:100", _("Last Purchase Price")+ ":Float:100", - - ] - return columns def get_bom_stock(filters): @@ -85,7 +87,12 @@ def get_bom_stock(filters): GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1) -def get_item_details(item_code): - items = frappe.db.sql("""select it.item_group, it.item_name, it.stock_uom, it.name, it.brand, it.description, it.manufacturer_part_no, it.manufacturer from tabItem it where it.item_code = %s""", item_code, as_dict=1) +def get_manufacturer_records(): + details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no, parent"]) + manufacture_details = frappe._dict() + for detail in details: + dic = manufacture_details.setdefault(detail.get('parent'), {}) + dic.setdefault('manufacturer', []).append(detail.get('manufacturer')) + dic.setdefault('manufacturer_part', []).append(detail.get('manufacturer_part_no')) - return dict((d.name, d) for d in items) + return manufacture_details \ No newline at end of file From a5b836d3d46605a8e24851c47177ee3c0d276520 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 17 Mar 2020 21:02:22 +0530 Subject: [PATCH 207/929] core: Added change log --- erpnext/change_log/v12/v12_6_0.md | 53 +++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 erpnext/change_log/v12/v12_6_0.md diff --git a/erpnext/change_log/v12/v12_6_0.md b/erpnext/change_log/v12/v12_6_0.md new file mode 100644 index 00000000000..b84e9bf0dc1 --- /dev/null +++ b/erpnext/change_log/v12/v12_6_0.md @@ -0,0 +1,53 @@ +## Version 12.6.0 Release Note + +### Enhancements + +- Deduction based on earnings/gross pay [#20935](https://github.com/frappe/erpnext/pull/20935) + +- If accounting dimension is tree-structured document and in report, filter is set based on group node, get result based on all children [#20860](https://github.com/frappe/erpnext/pull/20860) + +- Item alternative must have similar properties as original item [#20796](https://github.com/frappe/erpnext/pull/20796) + +- Show multiple manufacturers and part numbers in BOM Sock Calculated report [#19431](https://github.com/frappe/erpnext/pull/19431) + + +### Optimizations + +- Payment Reconciliation: search for customer's return invoices then filter out gl entries [#20710](https://github.com/frappe/erpnext/pull/20710) + +- Optimization of GL entry posting [#20676](https://github.com/frappe/erpnext/pull/20676) + +- Processing of serial numbers in Material Transfer [#20722](https://github.com/frappe/erpnext/pull/20722) + + +### Fixes: + +- Rate and amount in material request should not be copying from sales order [#20718](https://github.com/frappe/erpnext/pull/20718) + +- Update items after submission only if rate or qty changed [#20743](https://github.com/frappe/erpnext/pull/20743) + +- Odometer value was not syncing properly [#20451](https://github.com/frappe/erpnext/pull/20451) + +- Earn leave were not getting created when the max leaves allowed were set to 0 or less [#20535](https://github.com/frappe/erpnext/pull/20535) + +- Additional salary should not be created for inactive employee [#20686](https://github.com/frappe/erpnext/pull/20686) + +- Account dashboard was not working [#20715](https://github.com/frappe/erpnext/pull/20715) + +- Ignore mandatory fields while creating Material Request based on reorder level [#20720](https://github.com/frappe/erpnext/pull/20720) + +- Ignore permission when deleting linked emails in process of deletion of all company transactions [#20753](https://github.com/frappe/erpnext/pull/20753) + +- Total amount was not displaying in Journal Entry [#20794](https://github.com/frappe/erpnext/pull/20794) + +- HSN Code was not visible in GST itemised sales register [#20821](https://github.com/frappe/erpnext/pull/20821) + +- Validate Serial No/Batch No against unserialized item in Stock Reconciliation [#20858](https://github.com/frappe/erpnext/pull/20858) + +- Customer group filter resets on syncing invoices in offline POS [#20873](https://github.com/frappe/erpnext/pull/20873) + +- Purchase return were allowed even when assets are not cancelled [#20798](https://github.com/frappe/erpnext/pull/20798) + +- Reserved qty for production calculation if material transfer is skipped [#20900](https://github.com/frappe/erpnext/pull/20900) + +- Lock stock ledger entries that are being reposted to control concurrency [#20739](https://github.com/frappe/erpnext/pull/20739) \ No newline at end of file From da7c6b0d99b6d8bf52cb14978e5bb8b862d631fa Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 18 Mar 2020 11:29:02 +0530 Subject: [PATCH 208/929] fix: Missing if condition in Customer Default Bank Account Validation. (#20970) --- erpnext/selling/doctype/customer/customer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 3248c998b60..791a3e1536a 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -76,7 +76,8 @@ class Customer(TransactionBase): def validate_default_bank_account(self): if self.default_bank_account: is_company_account = frappe.db.get_value('Bank Account', self.default_bank_account, 'is_company_account') - frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account))) + if not is_company_account: + frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account))) def on_update(self): self.validate_name_with_customer_group() From e655648b18b9fd60516be4cef3959e14bdba0754 Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 18 Mar 2020 11:35:43 +0530 Subject: [PATCH 209/929] fix: Missing if condition in Customer Default Bank Account Validation. (#20971) --- erpnext/selling/doctype/customer/customer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 3248c998b60..791a3e1536a 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -76,7 +76,8 @@ class Customer(TransactionBase): def validate_default_bank_account(self): if self.default_bank_account: is_company_account = frappe.db.get_value('Bank Account', self.default_bank_account, 'is_company_account') - frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account))) + if not is_company_account: + frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account))) def on_update(self): self.validate_name_with_customer_group() From 1ed5993ff6f188201aa7c05440d56c479ad2e618 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 18 Mar 2020 13:33:55 +0530 Subject: [PATCH 210/929] fix: shortage qty in stock projected qty report --- .../stock/report/stock_projected_qty/stock_projected_qty.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py index 913d7d848ed..c8efb1637f9 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py @@ -44,7 +44,9 @@ def execute(filters=None): re_order_level = d.warehouse_reorder_level re_order_qty = d.warehouse_reorder_qty - shortage_qty = re_order_level - flt(bin.projected_qty) if (re_order_level or re_order_qty) else 0 + shortage_qty = 0 + if (re_order_level or re_order_qty) and re_order_level > bin.projected_qty: + shortage_qty = re_order_level - flt(bin.projected_qty) data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse, item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty, From d21a9fa91719a114edb2830b946c682fc8e661cc Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 18 Mar 2020 13:33:55 +0530 Subject: [PATCH 211/929] fix: shortage qty in stock projected qty report --- .../stock/report/stock_projected_qty/stock_projected_qty.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py index 913d7d848ed..c8efb1637f9 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py @@ -44,7 +44,9 @@ def execute(filters=None): re_order_level = d.warehouse_reorder_level re_order_qty = d.warehouse_reorder_qty - shortage_qty = re_order_level - flt(bin.projected_qty) if (re_order_level or re_order_qty) else 0 + shortage_qty = 0 + if (re_order_level or re_order_qty) and re_order_level > bin.projected_qty: + shortage_qty = re_order_level - flt(bin.projected_qty) data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse, item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty, From 210a718a0420ad88f28b2cc2679a8ce92ff2bd95 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 19 Mar 2020 10:03:06 +0530 Subject: [PATCH 212/929] fix: bom comparison issue --- erpnext/manufacturing/doctype/bom/bom.py | 4 ++++ .../bom_comparison_tool/bom_comparison_tool.js | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index b2a0b18db5e..19d36f6e8c4 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -823,6 +823,10 @@ def add_operations_cost(stock_entry, work_order=None, expense_account=None): def get_bom_diff(bom1, bom2): from frappe.model import table_fields + if bom1 == bom2: + frappe.throw(_("BOM 1 {0} and BOM 2 {1} should not be same") + .format(frappe.bold(bom1), frappe.bold(bom2))) + doc1 = frappe.get_doc('BOM', bom1) doc2 = frappe.get_doc('BOM', bom2) diff --git a/erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js b/erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js index 7152d3dff61..1bcb1efdaad 100644 --- a/erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js +++ b/erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js @@ -22,7 +22,14 @@ erpnext.BOMComparisonTool = class BOMComparisonTool { fieldname: 'name1', fieldtype: 'Link', options: 'BOM', - change: () => this.fetch_and_render() + change: () => this.fetch_and_render(), + get_query: () => { + return { + filters: { + "name": ["not in", [this.form.get_value("name2") || ""]] + } + } + } }, { fieldtype: 'Column Break' @@ -32,7 +39,14 @@ erpnext.BOMComparisonTool = class BOMComparisonTool { fieldname: 'name2', fieldtype: 'Link', options: 'BOM', - change: () => this.fetch_and_render() + change: () => this.fetch_and_render(), + get_query: () => { + return { + filters: { + "name": ["not in", [this.form.get_value("name1") || ""]] + } + } + } }, { fieldtype: 'Section Break' From 82661aa04294214035094b111a4344c70fd7d6b7 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 19 Mar 2020 10:03:06 +0530 Subject: [PATCH 213/929] fix: bom comparison issue --- erpnext/manufacturing/doctype/bom/bom.py | 4 ++++ .../bom_comparison_tool/bom_comparison_tool.js | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index b2a0b18db5e..19d36f6e8c4 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -823,6 +823,10 @@ def add_operations_cost(stock_entry, work_order=None, expense_account=None): def get_bom_diff(bom1, bom2): from frappe.model import table_fields + if bom1 == bom2: + frappe.throw(_("BOM 1 {0} and BOM 2 {1} should not be same") + .format(frappe.bold(bom1), frappe.bold(bom2))) + doc1 = frappe.get_doc('BOM', bom1) doc2 = frappe.get_doc('BOM', bom2) diff --git a/erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js b/erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js index 7152d3dff61..1bcb1efdaad 100644 --- a/erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js +++ b/erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js @@ -22,7 +22,14 @@ erpnext.BOMComparisonTool = class BOMComparisonTool { fieldname: 'name1', fieldtype: 'Link', options: 'BOM', - change: () => this.fetch_and_render() + change: () => this.fetch_and_render(), + get_query: () => { + return { + filters: { + "name": ["not in", [this.form.get_value("name2") || ""]] + } + } + } }, { fieldtype: 'Column Break' @@ -32,7 +39,14 @@ erpnext.BOMComparisonTool = class BOMComparisonTool { fieldname: 'name2', fieldtype: 'Link', options: 'BOM', - change: () => this.fetch_and_render() + change: () => this.fetch_and_render(), + get_query: () => { + return { + filters: { + "name": ["not in", [this.form.get_value("name1") || ""]] + } + } + } }, { fieldtype: 'Section Break' From bf0f9c5cb4d9367db5be706b0af33646752ce17f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 19 Mar 2020 10:58:07 +0530 Subject: [PATCH 214/929] fix: Multiple fixes during pre-release testing (#20986) --- .../doctype/journal_entry/journal_entry.py | 8 +- .../doctype/vehicle_log/test_vehicle_log.py | 7 +- erpnext/hr/doctype/vehicle_log/vehicle_log.js | 26 --- .../hr/doctype/vehicle_log/vehicle_log.json | 9 +- erpnext/hr/doctype/vehicle_log/vehicle_log.py | 54 ++--- .../vehicle_service/vehicle_service.json | 194 +++++------------- .../bom_stock_calculated.py | 4 +- 7 files changed, 84 insertions(+), 218 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 04c5c9ec679..1a530c75395 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -456,8 +456,10 @@ class JournalEntry(AccountsController): def set_print_format_fields(self): bank_amount = party_amount = total_amount = 0.0 currency = bank_account_currency = party_account_currency = pay_to_recd_from= None + party_type = None for d in self.get('accounts'): if d.party_type in ['Customer', 'Supplier'] and d.party: + party_type = d.party_type if not pay_to_recd_from: pay_to_recd_from = d.party @@ -469,9 +471,9 @@ class JournalEntry(AccountsController): bank_amount += (d.debit_in_account_currency or d.credit_in_account_currency) bank_account_currency = d.account_currency - if pay_to_recd_from: - self.pay_to_recd_from = frappe.db.get_value(d.party_type, pay_to_recd_from, - "customer_name" if d.party_type=="Customer" else "supplier_name") + if party_type and pay_to_recd_from: + self.pay_to_recd_from = frappe.db.get_value(party_type, pay_to_recd_from, + "customer_name" if party_type=="Customer" else "supplier_name") if bank_amount: total_amount = bank_amount currency = bank_account_currency diff --git a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py index 3770da73fcf..e9dc7764f7b 100644 --- a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py @@ -6,11 +6,14 @@ from __future__ import unicode_literals import frappe import unittest from frappe.utils import nowdate,flt, cstr,random_string -# test_records = frappe.get_test_records('Vehicle Log') + class TestVehicleLog(unittest.TestCase): def test_make_vehicle_log_and_syncing_of_odometer_value(self): - employee_id=frappe.db.sql("""select name from `tabEmployee` order by modified desc limit 1""")[0][0] + employee_id = frappe.db.sql("""select name from `tabEmployee` where status='Active' order by modified desc limit 1""") + employee_id = employee_id[0][0] if employee_id else None + license_plate = get_vehicle(employee_id) + vehicle_log = frappe.get_doc({ "doctype": "Vehicle Log", "license_plate": cstr(license_plate), diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.js b/erpnext/hr/doctype/vehicle_log/vehicle_log.js index bdb37d2b734..6f3a0dc40eb 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.js +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.js @@ -3,11 +3,6 @@ frappe.ui.form.on("Vehicle Log", { refresh: function(frm) { - - if(frm.doc.license_plate && frm.doc.__islocal){ - frm.events.set_vehicle_details(frm); - } - if(frm.doc.docstatus == 1) { frm.add_custom_button(__('Expense Claim'), function() { frm.events.expense_claim(frm); @@ -16,27 +11,6 @@ frappe.ui.form.on("Vehicle Log", { } }, - license_plate: function(frm) { - if(frm.doc.license_plate){ - frm.events.set_vehicle_details(frm); - } - }, - - set_vehicle_details: function(frm) { - frappe.call({ - method: "erpnext.hr.doctype.vehicle_log.vehicle_log.get_make_model", - args: { - license_plate: frm.doc.license_plate - }, - callback: function(r) { - frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "make", r.message[0]); - frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "model", r.message[1]); - frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "last_odometer", r.message[2]); - frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "employee", r.message[3]); - } - }); - }, - expense_claim: function(frm){ frappe.call({ method: "erpnext.hr.doctype.vehicle_log.vehicle_log.make_expense_claim", diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.json b/erpnext/hr/doctype/vehicle_log/vehicle_log.json index 52effffc064..619e295ebe8 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.json +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.json @@ -1,5 +1,4 @@ { - "actions": [], "autoname": "naming_series:", "creation": "2016-09-03 14:14:51.788550", "doctype": "DocType", @@ -56,6 +55,8 @@ "reqd": 1 }, { + "fetch_from": "license_plate.employee", + "fetch_if_empty": 1, "fieldname": "employee", "fieldtype": "Link", "in_list_view": 1, @@ -73,11 +74,13 @@ "fieldtype": "Column Break" }, { + "fetch_from": "license_plate.model", "fieldname": "model", "fieldtype": "Read Only", "label": "Model" }, { + "fetch_from": "license_plate.make", "fieldname": "make", "fieldtype": "Read Only", "label": "Make" @@ -152,6 +155,7 @@ "read_only": 1 }, { + "fetch_from": "license_plate.last_odometer", "fieldname": "last_odometer", "fieldtype": "Int", "label": "last Odometer Value ", @@ -164,8 +168,7 @@ } ], "is_submittable": 1, - "links": [], - "modified": "2020-01-28 12:43:34.419647", + "modified": "2020-03-18 16:45:45.060761", "modified_by": "Administrator", "module": "HR", "name": "Vehicle Log", diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.py b/erpnext/hr/doctype/vehicle_log/vehicle_log.py index 12cc1dd03a6..8affab2a18d 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.py @@ -12,18 +12,7 @@ from frappe.model.document import Document class VehicleLog(Document): def validate(self): if flt(self.odometer) < flt(self.last_odometer): - frappe.throw(_("Current Odometer reading entered should be greater than initial Vehicle Odometer {0}").format(self.last_odometer)) - for service_detail in self.service_detail: - if (service_detail.service_item or service_detail.type or service_detail.frequency or service_detail.expense_amount): - if not (service_detail.service_item and service_detail.type and service_detail.frequency and service_detail.expense_amount): - frappe.throw(_("Service Item,Type,frequency and expense amount are required")) - - def before_insert(self): - model_details = get_make_model(self.license_plate) - self.make = model_details[0] - self.model = model_details[1] - self.last_odometer = model_details[2] - self.employee = model_details[3] + frappe.throw(_("Current Odometer Value should be greater than Last Odometer Value {0}").format(self.last_odometer)) def on_submit(self): frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", self.odometer) @@ -34,35 +23,26 @@ class VehicleLog(Document): updated_odometer_value = int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", updated_odometer_value) -@frappe.whitelist() -def get_make_model(license_plate): - vehicle=frappe.get_doc("Vehicle",license_plate) - return (vehicle.make, vehicle.model, vehicle.last_odometer, vehicle.employee) - @frappe.whitelist() def make_expense_claim(docname): - def check_exp_claim_exists(): - exp_claim = frappe.db.sql("""select name from `tabExpense Claim` where vehicle_log=%s""",vehicle_log.name) - return exp_claim[0][0] if exp_claim else "" - def calc_service_exp(): - total_exp_amt=0 - exp_claim = check_exp_claim_exists() - if exp_claim: - frappe.throw(_("Expense Claim {0} already exists for the Vehicle Log").format(exp_claim)) - for serdetail in vehicle_log.service_detail: - total_exp_amt = total_exp_amt + serdetail.expense_amount - return total_exp_amt + expense_claim = frappe.db.exists("Expense Claim", {"vehicle_log": docname}) + if expense_claim: + frappe.throw(_("Expense Claim {0} already exists for the Vehicle Log").format(expense_claim)) vehicle_log = frappe.get_doc("Vehicle Log", docname) + service_expense = sum([flt(d.expense_amount) for d in vehicle_log.service_detail]) + + claim_amount = service_expense + flt(vehicle_log.price) + if not claim_amount: + frappe.throw(_("No additional expenses has been added")) + exp_claim = frappe.new_doc("Expense Claim") - exp_claim.employee=vehicle_log.employee - exp_claim.vehicle_log=vehicle_log.name - exp_claim.remark=_("Expense Claim for Vehicle Log {0}").format(vehicle_log.name) - fuel_price=vehicle_log.price - total_claim_amt=calc_service_exp() + fuel_price - exp_claim.append("expenses",{ - "expense_date":vehicle_log.date, - "description":_("Vehicle Expenses"), - "amount":total_claim_amt + exp_claim.employee = vehicle_log.employee + exp_claim.vehicle_log = vehicle_log.name + exp_claim.remark = _("Expense Claim for Vehicle Log {0}").format(vehicle_log.name) + exp_claim.append("expenses", { + "expense_date": vehicle_log.date, + "description": _("Vehicle Expenses"), + "amount": claim_amount }) return exp_claim.as_dict() diff --git a/erpnext/hr/doctype/vehicle_service/vehicle_service.json b/erpnext/hr/doctype/vehicle_service/vehicle_service.json index 7d9d0df44cc..e0bce2b6eec 100644 --- a/erpnext/hr/doctype/vehicle_service/vehicle_service.json +++ b/erpnext/hr/doctype/vehicle_service/vehicle_service.json @@ -1,153 +1,57 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-09-03 19:20:14.561962", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, + "creation": "2016-09-03 19:20:14.561962", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "service_item", + "type", + "frequency", + "expense_amount" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "service_item", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Service Item", - "length": 0, - "no_copy": 0, - "options": "\nBrake Oil\nBrake Pad\nClutch Plate\nEngine Oil\nOil Change\nWheels", - "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": "service_item", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Service Item", + "options": "\nBrake Oil\nBrake Pad\nClutch Plate\nEngine Oil\nOil Change\nWheels", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Type", - "length": 0, - "no_copy": 0, - "options": "\nInspection\nService\nChange", - "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": "type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Type", + "options": "\nInspection\nService\nChange", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "frequency", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Frequency", - "length": 0, - "no_copy": 0, - "options": "\nMileage\nMonthly\nQuarterly\nHalf Yearly\nYearly", - "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": "frequency", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Frequency", + "options": "\nMileage\nMonthly\nQuarterly\nHalf Yearly\nYearly", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expense_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Expense", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "expense_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Expense", + "reqd": 1 } - ], - "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": "2017-01-09 11:10:29.476907", - "modified_by": "Administrator", - "module": "HR", - "name": "Vehicle Service", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "istable": 1, + "modified": "2020-03-18 16:49:46.645004", + "modified_by": "Administrator", + "module": "HR", + "name": "Vehicle Service", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py index f7b407b7922..c1204d36152 100644 --- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py +++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py @@ -26,8 +26,8 @@ def get_report_data(last_pur_price, reqd_qty, row, manufacture_details): to_build = row.to_build if row.to_build > 0 else 0 diff_qty = to_build - reqd_qty return [row.item_code, row.description, - comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer', []), add_quotes=False), - comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer_part', []), add_quotes=False), + comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer', [])), + comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer_part', [])), row.actual_qty, str(to_build), reqd_qty, diff_qty, last_pur_price] From 7b45ae1f2dbc76db0fc5d72c8791161879ce5640 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 19 Mar 2020 10:58:41 +0530 Subject: [PATCH 215/929] fix: Multiple fixes during pre-release testing (#20985) --- .../doctype/journal_entry/journal_entry.py | 8 +- .../doctype/vehicle_log/test_vehicle_log.py | 7 +- erpnext/hr/doctype/vehicle_log/vehicle_log.js | 26 --- .../hr/doctype/vehicle_log/vehicle_log.json | 9 +- erpnext/hr/doctype/vehicle_log/vehicle_log.py | 54 ++--- .../vehicle_service/vehicle_service.json | 194 +++++------------- .../bom_stock_calculated.py | 4 +- 7 files changed, 84 insertions(+), 218 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 04c5c9ec679..1a530c75395 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -456,8 +456,10 @@ class JournalEntry(AccountsController): def set_print_format_fields(self): bank_amount = party_amount = total_amount = 0.0 currency = bank_account_currency = party_account_currency = pay_to_recd_from= None + party_type = None for d in self.get('accounts'): if d.party_type in ['Customer', 'Supplier'] and d.party: + party_type = d.party_type if not pay_to_recd_from: pay_to_recd_from = d.party @@ -469,9 +471,9 @@ class JournalEntry(AccountsController): bank_amount += (d.debit_in_account_currency or d.credit_in_account_currency) bank_account_currency = d.account_currency - if pay_to_recd_from: - self.pay_to_recd_from = frappe.db.get_value(d.party_type, pay_to_recd_from, - "customer_name" if d.party_type=="Customer" else "supplier_name") + if party_type and pay_to_recd_from: + self.pay_to_recd_from = frappe.db.get_value(party_type, pay_to_recd_from, + "customer_name" if party_type=="Customer" else "supplier_name") if bank_amount: total_amount = bank_amount currency = bank_account_currency diff --git a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py index 3770da73fcf..e9dc7764f7b 100644 --- a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py @@ -6,11 +6,14 @@ from __future__ import unicode_literals import frappe import unittest from frappe.utils import nowdate,flt, cstr,random_string -# test_records = frappe.get_test_records('Vehicle Log') + class TestVehicleLog(unittest.TestCase): def test_make_vehicle_log_and_syncing_of_odometer_value(self): - employee_id=frappe.db.sql("""select name from `tabEmployee` order by modified desc limit 1""")[0][0] + employee_id = frappe.db.sql("""select name from `tabEmployee` where status='Active' order by modified desc limit 1""") + employee_id = employee_id[0][0] if employee_id else None + license_plate = get_vehicle(employee_id) + vehicle_log = frappe.get_doc({ "doctype": "Vehicle Log", "license_plate": cstr(license_plate), diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.js b/erpnext/hr/doctype/vehicle_log/vehicle_log.js index bdb37d2b734..6f3a0dc40eb 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.js +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.js @@ -3,11 +3,6 @@ frappe.ui.form.on("Vehicle Log", { refresh: function(frm) { - - if(frm.doc.license_plate && frm.doc.__islocal){ - frm.events.set_vehicle_details(frm); - } - if(frm.doc.docstatus == 1) { frm.add_custom_button(__('Expense Claim'), function() { frm.events.expense_claim(frm); @@ -16,27 +11,6 @@ frappe.ui.form.on("Vehicle Log", { } }, - license_plate: function(frm) { - if(frm.doc.license_plate){ - frm.events.set_vehicle_details(frm); - } - }, - - set_vehicle_details: function(frm) { - frappe.call({ - method: "erpnext.hr.doctype.vehicle_log.vehicle_log.get_make_model", - args: { - license_plate: frm.doc.license_plate - }, - callback: function(r) { - frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "make", r.message[0]); - frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "model", r.message[1]); - frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "last_odometer", r.message[2]); - frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "employee", r.message[3]); - } - }); - }, - expense_claim: function(frm){ frappe.call({ method: "erpnext.hr.doctype.vehicle_log.vehicle_log.make_expense_claim", diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.json b/erpnext/hr/doctype/vehicle_log/vehicle_log.json index 52effffc064..619e295ebe8 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.json +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.json @@ -1,5 +1,4 @@ { - "actions": [], "autoname": "naming_series:", "creation": "2016-09-03 14:14:51.788550", "doctype": "DocType", @@ -56,6 +55,8 @@ "reqd": 1 }, { + "fetch_from": "license_plate.employee", + "fetch_if_empty": 1, "fieldname": "employee", "fieldtype": "Link", "in_list_view": 1, @@ -73,11 +74,13 @@ "fieldtype": "Column Break" }, { + "fetch_from": "license_plate.model", "fieldname": "model", "fieldtype": "Read Only", "label": "Model" }, { + "fetch_from": "license_plate.make", "fieldname": "make", "fieldtype": "Read Only", "label": "Make" @@ -152,6 +155,7 @@ "read_only": 1 }, { + "fetch_from": "license_plate.last_odometer", "fieldname": "last_odometer", "fieldtype": "Int", "label": "last Odometer Value ", @@ -164,8 +168,7 @@ } ], "is_submittable": 1, - "links": [], - "modified": "2020-01-28 12:43:34.419647", + "modified": "2020-03-18 16:45:45.060761", "modified_by": "Administrator", "module": "HR", "name": "Vehicle Log", diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.py b/erpnext/hr/doctype/vehicle_log/vehicle_log.py index 12cc1dd03a6..8affab2a18d 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.py @@ -12,18 +12,7 @@ from frappe.model.document import Document class VehicleLog(Document): def validate(self): if flt(self.odometer) < flt(self.last_odometer): - frappe.throw(_("Current Odometer reading entered should be greater than initial Vehicle Odometer {0}").format(self.last_odometer)) - for service_detail in self.service_detail: - if (service_detail.service_item or service_detail.type or service_detail.frequency or service_detail.expense_amount): - if not (service_detail.service_item and service_detail.type and service_detail.frequency and service_detail.expense_amount): - frappe.throw(_("Service Item,Type,frequency and expense amount are required")) - - def before_insert(self): - model_details = get_make_model(self.license_plate) - self.make = model_details[0] - self.model = model_details[1] - self.last_odometer = model_details[2] - self.employee = model_details[3] + frappe.throw(_("Current Odometer Value should be greater than Last Odometer Value {0}").format(self.last_odometer)) def on_submit(self): frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", self.odometer) @@ -34,35 +23,26 @@ class VehicleLog(Document): updated_odometer_value = int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", updated_odometer_value) -@frappe.whitelist() -def get_make_model(license_plate): - vehicle=frappe.get_doc("Vehicle",license_plate) - return (vehicle.make, vehicle.model, vehicle.last_odometer, vehicle.employee) - @frappe.whitelist() def make_expense_claim(docname): - def check_exp_claim_exists(): - exp_claim = frappe.db.sql("""select name from `tabExpense Claim` where vehicle_log=%s""",vehicle_log.name) - return exp_claim[0][0] if exp_claim else "" - def calc_service_exp(): - total_exp_amt=0 - exp_claim = check_exp_claim_exists() - if exp_claim: - frappe.throw(_("Expense Claim {0} already exists for the Vehicle Log").format(exp_claim)) - for serdetail in vehicle_log.service_detail: - total_exp_amt = total_exp_amt + serdetail.expense_amount - return total_exp_amt + expense_claim = frappe.db.exists("Expense Claim", {"vehicle_log": docname}) + if expense_claim: + frappe.throw(_("Expense Claim {0} already exists for the Vehicle Log").format(expense_claim)) vehicle_log = frappe.get_doc("Vehicle Log", docname) + service_expense = sum([flt(d.expense_amount) for d in vehicle_log.service_detail]) + + claim_amount = service_expense + flt(vehicle_log.price) + if not claim_amount: + frappe.throw(_("No additional expenses has been added")) + exp_claim = frappe.new_doc("Expense Claim") - exp_claim.employee=vehicle_log.employee - exp_claim.vehicle_log=vehicle_log.name - exp_claim.remark=_("Expense Claim for Vehicle Log {0}").format(vehicle_log.name) - fuel_price=vehicle_log.price - total_claim_amt=calc_service_exp() + fuel_price - exp_claim.append("expenses",{ - "expense_date":vehicle_log.date, - "description":_("Vehicle Expenses"), - "amount":total_claim_amt + exp_claim.employee = vehicle_log.employee + exp_claim.vehicle_log = vehicle_log.name + exp_claim.remark = _("Expense Claim for Vehicle Log {0}").format(vehicle_log.name) + exp_claim.append("expenses", { + "expense_date": vehicle_log.date, + "description": _("Vehicle Expenses"), + "amount": claim_amount }) return exp_claim.as_dict() diff --git a/erpnext/hr/doctype/vehicle_service/vehicle_service.json b/erpnext/hr/doctype/vehicle_service/vehicle_service.json index 7d9d0df44cc..e0bce2b6eec 100644 --- a/erpnext/hr/doctype/vehicle_service/vehicle_service.json +++ b/erpnext/hr/doctype/vehicle_service/vehicle_service.json @@ -1,153 +1,57 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-09-03 19:20:14.561962", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, + "creation": "2016-09-03 19:20:14.561962", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "service_item", + "type", + "frequency", + "expense_amount" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "service_item", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Service Item", - "length": 0, - "no_copy": 0, - "options": "\nBrake Oil\nBrake Pad\nClutch Plate\nEngine Oil\nOil Change\nWheels", - "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": "service_item", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Service Item", + "options": "\nBrake Oil\nBrake Pad\nClutch Plate\nEngine Oil\nOil Change\nWheels", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Type", - "length": 0, - "no_copy": 0, - "options": "\nInspection\nService\nChange", - "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": "type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Type", + "options": "\nInspection\nService\nChange", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "frequency", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Frequency", - "length": 0, - "no_copy": 0, - "options": "\nMileage\nMonthly\nQuarterly\nHalf Yearly\nYearly", - "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": "frequency", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Frequency", + "options": "\nMileage\nMonthly\nQuarterly\nHalf Yearly\nYearly", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expense_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Expense", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "expense_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Expense", + "reqd": 1 } - ], - "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": "2017-01-09 11:10:29.476907", - "modified_by": "Administrator", - "module": "HR", - "name": "Vehicle Service", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "istable": 1, + "modified": "2020-03-18 16:49:46.645004", + "modified_by": "Administrator", + "module": "HR", + "name": "Vehicle Service", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py index f7b407b7922..c1204d36152 100644 --- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py +++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py @@ -26,8 +26,8 @@ def get_report_data(last_pur_price, reqd_qty, row, manufacture_details): to_build = row.to_build if row.to_build > 0 else 0 diff_qty = to_build - reqd_qty return [row.item_code, row.description, - comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer', []), add_quotes=False), - comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer_part', []), add_quotes=False), + comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer', [])), + comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer_part', [])), row.actual_qty, str(to_build), reqd_qty, diff_qty, last_pur_price] From 71e9f23d7755c0a4243be13a5932c1d7c2e26fde Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 19 Mar 2020 11:00:55 +0530 Subject: [PATCH 216/929] fix: Update is_tree field in Tree doctypes (#20982) --- erpnext/accounts/doctype/account/account.json | 6 +- .../doctype/cost_center/cost_center.json | 4 +- erpnext/assets/doctype/location/location.json | 4 +- .../assessment_group/assessment_group.json | 335 +++---------- .../healthcare_service_unit.json | 406 ++------------- erpnext/hr/doctype/department/department.json | 472 ++---------------- erpnext/projects/doctype/task/task.json | 6 +- .../quality_procedure/quality_procedure.json | 6 +- erpnext/setup/doctype/company/company.json | 6 +- .../customer_group/customer_group.json | 4 +- .../setup/doctype/item_group/item_group.json | 4 +- .../doctype/sales_person/sales_person.json | 4 +- .../supplier_group/supplier_group.json | 384 ++------------ .../setup/doctype/territory/territory.json | 4 +- .../stock/doctype/warehouse/warehouse.json | 4 +- 15 files changed, 226 insertions(+), 1423 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index 64efc2775a6..6a1183546f3 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_copy": 1, "allow_import": 1, "creation": "2013-01-30 12:49:46", @@ -196,10 +197,13 @@ ], "icon": "fa fa-money", "idx": 1, - "modified": "2019-10-10 19:10:02.967554", + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:26:03.992861", "modified_by": "Administrator", "module": "Accounts", "name": "Account", + "nsm_parent_field": "parent_account", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index 976f05ad637..126aa543185 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -124,11 +124,13 @@ ], "icon": "fa fa-money", "idx": 1, + "is_tree": 1, "links": [], - "modified": "2020-01-28 13:50:23.430434", + "modified": "2020-03-18 18:26:01.540170", "modified_by": "Administrator", "module": "Accounts", "name": "Cost Center", + "nsm_parent_field": "parent_cost_center", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/assets/doctype/location/location.json b/erpnext/assets/doctype/location/location.json index 5ecc72ab913..58f59701960 100644 --- a/erpnext/assets/doctype/location/location.json +++ b/erpnext/assets/doctype/location/location.json @@ -139,12 +139,14 @@ "read_only": 1 } ], + "is_tree": 1, "links": [], - "modified": "2020-01-28 13:52:22.513425", + "modified": "2020-03-18 18:25:59.283057", "modified_by": "Administrator", "module": "Assets", "name": "Location", "name_case": "Title Case", + "nsm_parent_field": "parent_location", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/education/doctype/assessment_group/assessment_group.json b/erpnext/education/doctype/assessment_group/assessment_group.json index 56917d20526..a341938a101 100644 --- a/erpnext/education/doctype/assessment_group/assessment_group.json +++ b/erpnext/education/doctype/assessment_group/assessment_group.json @@ -1,274 +1,91 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:assessment_group_name", - "beta": 0, - "creation": "2016-08-04 04:42:48.319388", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:assessment_group_name", + "creation": "2016-08-04 04:42:48.319388", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "assessment_group_name", + "is_group", + "section_break_2", + "parent_assessment_group", + "lft", + "rgt", + "old_parent" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "assessment_group_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": "Assessment Group 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 - }, + "fieldname": "assessment_group_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Assessment Group Name", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_group", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Group", - "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 - }, + "default": "0", + "fieldname": "is_group", + "fieldtype": "Check", + "label": "Is Group" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "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, - "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": "section_break_2", + "fieldtype": "Section Break", + "hidden": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "parent_assessment_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Parent Assessment Group", - "length": 0, - "no_copy": 0, - "options": "Assessment Group", - "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 - }, + "fieldname": "parent_assessment_group", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Parent Assessment Group", + "options": "Assessment Group", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lft", - "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": "lft", - "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": "lft", + "fieldtype": "Int", + "label": "lft" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rgt", - "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": "rgt", - "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": "rgt", + "fieldtype": "Int", + "label": "rgt" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "old_parent", - "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": "old_parent", - "length": 0, - "no_copy": 0, - "options": "Assessment Group", - "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": "old_parent", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "old_parent", + "options": "Assessment Group" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-11-10 19:09:25.366400", - "modified_by": "Administrator", - "module": "Education", - "name": "Assessment Group", - "name_case": "", - "owner": "Administrator", + ], + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:26:07.834596", + "modified_by": "Administrator", + "module": "Education", + "name": "Assessment Group", + "nsm_parent_field": "parent_assessment_group", + "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": "Academics User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "quick_entry": 1, + "restrict_to_domain": "Education", + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json index 6e92455404f..15d976530b2 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json @@ -1,542 +1,206 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:healthcare_service_unit_name", "beta": 1, "creation": "2016-09-21 13:48:14.731437", - "custom": 0, "description": "Healthcare Service Unit", - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "healthcare_service_unit_name", + "parent_healthcare_service_unit", + "is_group", + "service_unit_type", + "allow_appointments", + "overlap_appointments", + "inpatient_occupancy", + "occupancy_status", + "warehouse", + "company", + "lft", + "rgt", + "old_parent" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "healthcare_service_unit_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, "in_list_view": 1, - "in_standard_filter": 0, "label": "Service Unit", - "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": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "fieldname": "parent_healthcare_service_unit", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Parent Service Unit", - "length": 0, - "no_copy": 0, - "options": "Healthcare Service Unit", - "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": "Healthcare Service Unit" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "default": "0", "depends_on": "eval:doc.inpatient_occupancy != 1 && doc.allow_appointments != 1", "fieldname": "is_group", "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": "Is Group", - "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": "Is Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.is_group != 1", "fieldname": "service_unit_type", "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": "Service Unit Type", - "length": 0, - "no_copy": 0, - "options": "Healthcare Service Unit Type", - "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": "Healthcare Service Unit Type" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "default": "0", "depends_on": "eval:doc.is_group != 1 && doc.inpatient_occupancy != 1", "fetch_from": "service_unit_type.allow_appointments", "fieldname": "allow_appointments", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Allow Appointments", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", "depends_on": "eval:doc.is_group != 1 && doc.allow_appointments == 1 && doc.inpatient_occupany != 1", "fetch_from": "service_unit_type.overlap_appointments", "fieldname": "overlap_appointments", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Allow Overlap", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "default": "0", "depends_on": "eval:doc.allow_appointments != 1 && doc.is_group != 1", "fetch_from": "service_unit_type.inpatient_occupancy", "fieldname": "inpatient_occupancy", "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": "Inpatient Occupancy", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 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, - "default": "", "depends_on": "eval:doc.inpatient_occupancy == 1", "fieldname": "occupancy_status", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Occupancy Status", - "length": 0, "no_copy": 1, "options": "Vacant\nOccupied", - "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": 1, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.is_group != 1", "fieldname": "warehouse", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Warehouse", - "length": 0, "no_copy": 1, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Warehouse" }, { - "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": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Company", - "length": 0, - "no_copy": 0, "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, "remember_last_selected_value": 1, - "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": "lft", "fieldtype": "Int", "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": "lft", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 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": "rgt", "fieldtype": "Int", "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": "rgt", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 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": "old_parent", "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Old Parent", - "length": 0, "no_copy": 1, "options": "Healthcare Service Unit", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-10-04 21:09:52.261882", + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:26:06.360941", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit", - "name_case": "", + "nsm_parent_field": "parent_healthcare_service_unit", "owner": "Administrator", "permissions": [ { - "amend": 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": "Nursing User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "share": 1 }, { - "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": "Healthcare Administrator", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, - "delete": 0, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Physician", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Healthcare", "search_fields": "healthcare_service_unit_name", - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", "title_field": "healthcare_service_unit_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/department/department.json b/erpnext/hr/doctype/department/department.json index 3b400ce8d76..2de391efaaa 100644 --- a/erpnext/hr/doctype/department/department.json +++ b/erpnext/hr/doctype/department/department.json @@ -1,569 +1,177 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, - "autoname": "", - "beta": 0, "creation": "2013-02-05 11:48:26", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, + "engine": "InnoDB", + "field_order": [ + "department_name", + "parent_department", + "company", + "is_group", + "disabled", + "section_break_4", + "leave_block_list", + "leave_section", + "leave_approvers", + "expense_section", + "expense_approvers", + "lft", + "rgt", + "old_parent" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "department_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": "Department", - "length": 0, - "no_copy": 0, "oldfieldname": "department_name", "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": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "parent_department", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Parent 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": 0, - "columns": 0, - "fetch_if_empty": 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": 1, "label": "Company", - "length": 0, - "no_copy": 0, "options": "Company", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "is_group", "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": "Is Group", - "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": "Is Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 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": 0, - "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, - "fetch_if_empty": 0, "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Days for which Holidays are blocked for this department.", - "fetch_if_empty": 0, "fieldname": "leave_block_list", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Leave Block List", - "length": 0, - "no_copy": 0, - "options": "Leave Block 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": "Leave Block List" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "leave_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": "Leave Approvers", - "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": "Leave Approvers" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "The first Leave Approver in the list will be set as the default Leave Approver.", - "fetch_if_empty": 0, "fieldname": "leave_approvers", "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": "Leave Approver", - "length": 0, - "no_copy": 0, - "options": "Department Approver", - "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 Approver" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "expense_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": "Expense Approvers", - "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": "Expense Approvers" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "The first Expense Approver in the list will be set as the default Expense Approver.", - "fetch_if_empty": 0, "fieldname": "expense_approvers", "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": "Expense Approver", - "length": 0, - "no_copy": 0, - "options": "Department Approver", - "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 Approver" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "lft", "fieldtype": "Int", "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": "lft", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "rgt", "fieldtype": "Int", "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": "rgt", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "old_parent", "fieldtype": "Data", "hidden": 1, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Old Parent", - "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 + "print_hide": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-sitemap", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-06-25 18:43:05.550387", + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:26:05.966022", "modified_by": "Administrator", "module": "HR", "name": "Department", + "nsm_parent_field": "parent_department", "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": "HR User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "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": "Academics User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "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": "HR Manager", "set_user_permissions": 1, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "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/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index 794d8161d2f..ce0dbd179ce 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "TASK-.YYYY.-.#####", "creation": "2013-01-29 19:25:50", @@ -361,11 +362,14 @@ ], "icon": "fa fa-check", "idx": 1, + "is_tree": 1, + "links": [], "max_attachments": 5, - "modified": "2019-09-10 13:46:24.631754", + "modified": "2020-03-18 18:26:04.788061", "modified_by": "Administrator", "module": "Projects", "name": "Task", + "nsm_parent_field": "parent_task", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json index 472b75103cc..e44e0733967 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "format:PRC-{quality_procedure_name}", "creation": "2018-10-06 00:06:29.756804", "doctype": "DocType", @@ -69,10 +70,13 @@ "reqd": 1 } ], - "modified": "2019-08-05 13:09:29.945082", + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:26:05.511984", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure", + "nsm_parent_field": "parent_quality_procedure", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index dd602eca103..5ce51a6e8d9 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:company_name", @@ -725,10 +726,13 @@ "icon": "fa fa-building", "idx": 1, "image_field": "company_logo", - "modified": "2019-11-22 13:04:47.470768", + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:26:03.145312", "modified_by": "Administrator", "module": "Setup", "name": "Company", + "nsm_parent_field": "parent_company", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/setup/doctype/customer_group/customer_group.json b/erpnext/setup/doctype/customer_group/customer_group.json index 7fa242ae19d..82d5f7a3f73 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.json +++ b/erpnext/setup/doctype/customer_group/customer_group.json @@ -137,11 +137,13 @@ ], "icon": "fa fa-sitemap", "idx": 1, + "is_tree": 1, "links": [], - "modified": "2020-01-28 13:49:23.961708", + "modified": "2020-03-18 18:26:02.536305", "modified_by": "Administrator", "module": "Setup", "name": "Customer Group", + "nsm_parent_field": "parent_customer_group", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/setup/doctype/item_group/item_group.json b/erpnext/setup/doctype/item_group/item_group.json index 36e3e68ef43..849fb6fd911 100644 --- a/erpnext/setup/doctype/item_group/item_group.json +++ b/erpnext/setup/doctype/item_group/item_group.json @@ -185,13 +185,15 @@ "icon": "fa fa-sitemap", "idx": 1, "image_field": "image", + "is_tree": 1, "links": [], "max_attachments": 3, - "modified": "2020-01-28 13:51:05.456014", + "modified": "2020-03-18 18:25:59.979083", "modified_by": "Administrator", "module": "Setup", "name": "Item Group", "name_case": "Title Case", + "nsm_parent_field": "parent_item_group", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/setup/doctype/sales_person/sales_person.json b/erpnext/setup/doctype/sales_person/sales_person.json index b05365d2ca9..d7a9d5dfba1 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.json +++ b/erpnext/setup/doctype/sales_person/sales_person.json @@ -143,11 +143,13 @@ ], "icon": "icon-user", "idx": 1, + "is_tree": 1, "links": [], - "modified": "2020-01-28 13:50:31.891050", + "modified": "2020-03-18 18:26:01.076203", "modified_by": "Administrator", "module": "Setup", "name": "Sales Person", + "nsm_parent_field": "parent_sales_person", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.json b/erpnext/setup/doctype/supplier_group/supplier_group.json index 5c413341935..b7989d4ffad 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.json +++ b/erpnext/setup/doctype/supplier_group/supplier_group.json @@ -1,481 +1,165 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:supplier_group_name", - "beta": 0, "creation": "2013-01-10 16:34:24", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, + "engine": "InnoDB", + "field_order": [ + "supplier_group_name", + "parent_supplier_group", + "is_group", + "section_credit_limit", + "payment_terms", + "default_payable_account", + "accounts", + "lft", + "rgt", + "old_parent" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "supplier_group_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": "Supplier Group Name", - "length": 0, - "no_copy": 0, "oldfieldname": "supplier_type", "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": 1, - "collapsible": 0, - "columns": 0, "fieldname": "parent_supplier_group", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Parent Supplier Group", - "length": 0, - "no_copy": 0, - "options": "Supplier Group", - "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": "Supplier Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "is_group", "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": "Is Group", - "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": "Is Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "section_credit_limit", "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": "Credit Limit", - "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": "Credit Limit" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "payment_terms", "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 Payment Terms Template", - "length": 0, - "no_copy": 0, - "options": "Payment Terms 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": "Payment Terms Template" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "default_payable_account", "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": "Default Payable Account", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Default Payable Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:!doc.__islocal", "description": "Mention if non-standard receivable account applicable", "fieldname": "accounts", "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": "Accounts", - "length": 0, - "no_copy": 0, - "options": "Party Account", - "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": "Party Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "lft", "fieldtype": "Int", "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": "lft", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, "report_hide": 1, - "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": "rgt", "fieldtype": "Int", "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": "rgt", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, "report_hide": 1, - "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": "old_parent", "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Old Parent", - "length": 0, "no_copy": 1, "options": "Supplier Group", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-flag", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-29 06:25:57.589824", + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:26:07.179330", "modified_by": "Administrator", "module": "Setup", "name": "Supplier Group", + "nsm_parent_field": "parent_supplier_group", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Purchase Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Purchase Manager" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Purchase User" }, { - "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": "Purchase Master Manager", "set_user_permissions": 1, "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": 0, "role": "Purchase Master Manager", - "set_user_permissions": 0, - "share": 0, - "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": 0, - "role": "Purchase Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Purchase Manager" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, - "report": 0, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Purchase User" } ], - "quick_entry": 0, - "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/territory/territory.json b/erpnext/setup/doctype/territory/territory.json index 91a3dda2bb0..4277fe75ec4 100644 --- a/erpnext/setup/doctype/territory/territory.json +++ b/erpnext/setup/doctype/territory/territory.json @@ -121,12 +121,14 @@ ], "icon": "fa fa-map-marker", "idx": 1, + "is_tree": 1, "links": [], - "modified": "2020-01-28 13:49:31.905800", + "modified": "2020-03-18 18:26:02.055750", "modified_by": "Administrator", "module": "Setup", "name": "Territory", "name_case": "Title Case", + "nsm_parent_field": "parent_territory", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json index 2bf1ed89838..0606d0bdc64 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.json +++ b/erpnext/stock/doctype/warehouse/warehouse.json @@ -228,11 +228,13 @@ ], "icon": "fa fa-building", "idx": 1, + "is_tree": 1, "links": [], - "modified": "2020-01-28 13:50:59.368846", + "modified": "2020-03-18 18:26:00.479541", "modified_by": "Administrator", "module": "Stock", "name": "Warehouse", + "nsm_parent_field": "parent_warehouse", "owner": "Administrator", "permissions": [ { From 7c0398c8e3503baa44cdd4037f7d20f3c91ee4ee Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 19 Mar 2020 11:01:40 +0530 Subject: [PATCH 217/929] fix: Update is_tree field in Tree doctypes (#20983) --- erpnext/accounts/doctype/account/account.json | 6 +- .../doctype/cost_center/cost_center.json | 4 +- erpnext/assets/doctype/location/location.json | 4 +- .../assessment_group/assessment_group.json | 335 +++---------- .../healthcare_service_unit.json | 406 ++------------- erpnext/hr/doctype/department/department.json | 472 ++---------------- erpnext/projects/doctype/task/task.json | 6 +- .../quality_procedure/quality_procedure.json | 6 +- erpnext/setup/doctype/company/company.json | 6 +- .../customer_group/customer_group.json | 4 +- .../setup/doctype/item_group/item_group.json | 4 +- .../doctype/sales_person/sales_person.json | 4 +- .../supplier_group/supplier_group.json | 384 ++------------ .../setup/doctype/territory/territory.json | 4 +- .../stock/doctype/warehouse/warehouse.json | 4 +- 15 files changed, 226 insertions(+), 1423 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index 64efc2775a6..066aba0bf95 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_copy": 1, "allow_import": 1, "creation": "2013-01-30 12:49:46", @@ -196,10 +197,13 @@ ], "icon": "fa fa-money", "idx": 1, - "modified": "2019-10-10 19:10:02.967554", + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:35:45.618817", "modified_by": "Administrator", "module": "Accounts", "name": "Account", + "nsm_parent_field": "parent_account", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index 976f05ad637..8c94e0f87ad 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -124,11 +124,13 @@ ], "icon": "fa fa-money", "idx": 1, + "is_tree": 1, "links": [], - "modified": "2020-01-28 13:50:23.430434", + "modified": "2020-03-18 18:35:43.334214", "modified_by": "Administrator", "module": "Accounts", "name": "Cost Center", + "nsm_parent_field": "parent_cost_center", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/assets/doctype/location/location.json b/erpnext/assets/doctype/location/location.json index 5ecc72ab913..09f84960993 100644 --- a/erpnext/assets/doctype/location/location.json +++ b/erpnext/assets/doctype/location/location.json @@ -139,12 +139,14 @@ "read_only": 1 } ], + "is_tree": 1, "links": [], - "modified": "2020-01-28 13:52:22.513425", + "modified": "2020-03-18 18:35:41.062417", "modified_by": "Administrator", "module": "Assets", "name": "Location", "name_case": "Title Case", + "nsm_parent_field": "parent_location", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/education/doctype/assessment_group/assessment_group.json b/erpnext/education/doctype/assessment_group/assessment_group.json index 56917d20526..f0baf8a1013 100644 --- a/erpnext/education/doctype/assessment_group/assessment_group.json +++ b/erpnext/education/doctype/assessment_group/assessment_group.json @@ -1,274 +1,91 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:assessment_group_name", - "beta": 0, - "creation": "2016-08-04 04:42:48.319388", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:assessment_group_name", + "creation": "2016-08-04 04:42:48.319388", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "assessment_group_name", + "is_group", + "section_break_2", + "parent_assessment_group", + "lft", + "rgt", + "old_parent" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "assessment_group_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": "Assessment Group 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 - }, + "fieldname": "assessment_group_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Assessment Group Name", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_group", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Group", - "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 - }, + "default": "0", + "fieldname": "is_group", + "fieldtype": "Check", + "label": "Is Group" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "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, - "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": "section_break_2", + "fieldtype": "Section Break", + "hidden": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "parent_assessment_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Parent Assessment Group", - "length": 0, - "no_copy": 0, - "options": "Assessment Group", - "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 - }, + "fieldname": "parent_assessment_group", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Parent Assessment Group", + "options": "Assessment Group", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lft", - "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": "lft", - "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": "lft", + "fieldtype": "Int", + "label": "lft" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rgt", - "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": "rgt", - "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": "rgt", + "fieldtype": "Int", + "label": "rgt" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "old_parent", - "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": "old_parent", - "length": 0, - "no_copy": 0, - "options": "Assessment Group", - "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": "old_parent", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "old_parent", + "options": "Assessment Group" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-11-10 19:09:25.366400", - "modified_by": "Administrator", - "module": "Education", - "name": "Assessment Group", - "name_case": "", - "owner": "Administrator", + ], + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:35:48.798477", + "modified_by": "Administrator", + "module": "Education", + "name": "Assessment Group", + "nsm_parent_field": "parent_assessment_group", + "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": "Academics User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "quick_entry": 1, + "restrict_to_domain": "Education", + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json index 6e92455404f..5e12b5d95b9 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json @@ -1,542 +1,206 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:healthcare_service_unit_name", "beta": 1, "creation": "2016-09-21 13:48:14.731437", - "custom": 0, "description": "Healthcare Service Unit", - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "healthcare_service_unit_name", + "parent_healthcare_service_unit", + "is_group", + "service_unit_type", + "allow_appointments", + "overlap_appointments", + "inpatient_occupancy", + "occupancy_status", + "warehouse", + "company", + "lft", + "rgt", + "old_parent" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "healthcare_service_unit_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, "in_list_view": 1, - "in_standard_filter": 0, "label": "Service Unit", - "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": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "fieldname": "parent_healthcare_service_unit", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Parent Service Unit", - "length": 0, - "no_copy": 0, - "options": "Healthcare Service Unit", - "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": "Healthcare Service Unit" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "default": "0", "depends_on": "eval:doc.inpatient_occupancy != 1 && doc.allow_appointments != 1", "fieldname": "is_group", "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": "Is Group", - "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": "Is Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.is_group != 1", "fieldname": "service_unit_type", "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": "Service Unit Type", - "length": 0, - "no_copy": 0, - "options": "Healthcare Service Unit Type", - "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": "Healthcare Service Unit Type" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "default": "0", "depends_on": "eval:doc.is_group != 1 && doc.inpatient_occupancy != 1", "fetch_from": "service_unit_type.allow_appointments", "fieldname": "allow_appointments", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Allow Appointments", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", "depends_on": "eval:doc.is_group != 1 && doc.allow_appointments == 1 && doc.inpatient_occupany != 1", "fetch_from": "service_unit_type.overlap_appointments", "fieldname": "overlap_appointments", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Allow Overlap", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "default": "0", "depends_on": "eval:doc.allow_appointments != 1 && doc.is_group != 1", "fetch_from": "service_unit_type.inpatient_occupancy", "fieldname": "inpatient_occupancy", "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": "Inpatient Occupancy", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 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, - "default": "", "depends_on": "eval:doc.inpatient_occupancy == 1", "fieldname": "occupancy_status", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Occupancy Status", - "length": 0, "no_copy": 1, "options": "Vacant\nOccupied", - "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": 1, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.is_group != 1", "fieldname": "warehouse", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Warehouse", - "length": 0, "no_copy": 1, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Warehouse" }, { - "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": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Company", - "length": 0, - "no_copy": 0, "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, "remember_last_selected_value": 1, - "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": "lft", "fieldtype": "Int", "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": "lft", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 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": "rgt", "fieldtype": "Int", "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": "rgt", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 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": "old_parent", "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Old Parent", - "length": 0, "no_copy": 1, "options": "Healthcare Service Unit", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-10-04 21:09:52.261882", + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:35:47.811627", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit", - "name_case": "", + "nsm_parent_field": "parent_healthcare_service_unit", "owner": "Administrator", "permissions": [ { - "amend": 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": "Nursing User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "share": 1 }, { - "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": "Healthcare Administrator", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, - "delete": 0, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Physician", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Healthcare", "search_fields": "healthcare_service_unit_name", - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", "title_field": "healthcare_service_unit_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/department/department.json b/erpnext/hr/doctype/department/department.json index 3b400ce8d76..fcf26025681 100644 --- a/erpnext/hr/doctype/department/department.json +++ b/erpnext/hr/doctype/department/department.json @@ -1,569 +1,177 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, - "autoname": "", - "beta": 0, "creation": "2013-02-05 11:48:26", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, + "engine": "InnoDB", + "field_order": [ + "department_name", + "parent_department", + "company", + "is_group", + "disabled", + "section_break_4", + "leave_block_list", + "leave_section", + "leave_approvers", + "expense_section", + "expense_approvers", + "lft", + "rgt", + "old_parent" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "department_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": "Department", - "length": 0, - "no_copy": 0, "oldfieldname": "department_name", "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": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "parent_department", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Parent 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": 0, - "columns": 0, - "fetch_if_empty": 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": 1, "label": "Company", - "length": 0, - "no_copy": 0, "options": "Company", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "is_group", "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": "Is Group", - "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": "Is Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 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": 0, - "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, - "fetch_if_empty": 0, "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Days for which Holidays are blocked for this department.", - "fetch_if_empty": 0, "fieldname": "leave_block_list", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Leave Block List", - "length": 0, - "no_copy": 0, - "options": "Leave Block 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": "Leave Block List" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "leave_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": "Leave Approvers", - "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": "Leave Approvers" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "The first Leave Approver in the list will be set as the default Leave Approver.", - "fetch_if_empty": 0, "fieldname": "leave_approvers", "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": "Leave Approver", - "length": 0, - "no_copy": 0, - "options": "Department Approver", - "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 Approver" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "expense_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": "Expense Approvers", - "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": "Expense Approvers" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "The first Expense Approver in the list will be set as the default Expense Approver.", - "fetch_if_empty": 0, "fieldname": "expense_approvers", "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": "Expense Approver", - "length": 0, - "no_copy": 0, - "options": "Department Approver", - "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 Approver" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "lft", "fieldtype": "Int", "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": "lft", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "rgt", "fieldtype": "Int", "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": "rgt", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "old_parent", "fieldtype": "Data", "hidden": 1, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Old Parent", - "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 + "print_hide": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-sitemap", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-06-25 18:43:05.550387", + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:35:47.295588", "modified_by": "Administrator", "module": "HR", "name": "Department", + "nsm_parent_field": "parent_department", "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": "HR User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "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": "Academics User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "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": "HR Manager", "set_user_permissions": 1, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "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/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index 794d8161d2f..b5c2c40b970 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "TASK-.YYYY.-.#####", "creation": "2013-01-29 19:25:50", @@ -361,11 +362,14 @@ ], "icon": "fa fa-check", "idx": 1, + "is_tree": 1, + "links": [], "max_attachments": 5, - "modified": "2019-09-10 13:46:24.631754", + "modified": "2020-03-18 18:35:46.151641", "modified_by": "Administrator", "module": "Projects", "name": "Task", + "nsm_parent_field": "parent_task", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json index 472b75103cc..21b11ce1bc9 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "format:PRC-{quality_procedure_name}", "creation": "2018-10-06 00:06:29.756804", "doctype": "DocType", @@ -69,10 +70,13 @@ "reqd": 1 } ], - "modified": "2019-08-05 13:09:29.945082", + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:35:46.824714", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure", + "nsm_parent_field": "parent_quality_procedure", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index dd602eca103..58a5bdfa883 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:company_name", @@ -725,10 +726,13 @@ "icon": "fa fa-building", "idx": 1, "image_field": "company_logo", - "modified": "2019-11-22 13:04:47.470768", + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:35:44.745792", "modified_by": "Administrator", "module": "Setup", "name": "Company", + "nsm_parent_field": "parent_company", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/setup/doctype/customer_group/customer_group.json b/erpnext/setup/doctype/customer_group/customer_group.json index 7fa242ae19d..0dd3e5d9811 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.json +++ b/erpnext/setup/doctype/customer_group/customer_group.json @@ -137,11 +137,13 @@ ], "icon": "fa fa-sitemap", "idx": 1, + "is_tree": 1, "links": [], - "modified": "2020-01-28 13:49:23.961708", + "modified": "2020-03-18 18:35:44.263595", "modified_by": "Administrator", "module": "Setup", "name": "Customer Group", + "nsm_parent_field": "parent_customer_group", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/setup/doctype/item_group/item_group.json b/erpnext/setup/doctype/item_group/item_group.json index 36e3e68ef43..d9b3a290cbf 100644 --- a/erpnext/setup/doctype/item_group/item_group.json +++ b/erpnext/setup/doctype/item_group/item_group.json @@ -185,13 +185,15 @@ "icon": "fa fa-sitemap", "idx": 1, "image_field": "image", + "is_tree": 1, "links": [], "max_attachments": 3, - "modified": "2020-01-28 13:51:05.456014", + "modified": "2020-03-18 18:35:41.694677", "modified_by": "Administrator", "module": "Setup", "name": "Item Group", "name_case": "Title Case", + "nsm_parent_field": "parent_item_group", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/setup/doctype/sales_person/sales_person.json b/erpnext/setup/doctype/sales_person/sales_person.json index b05365d2ca9..bdb76e7d233 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.json +++ b/erpnext/setup/doctype/sales_person/sales_person.json @@ -143,11 +143,13 @@ ], "icon": "icon-user", "idx": 1, + "is_tree": 1, "links": [], - "modified": "2020-01-28 13:50:31.891050", + "modified": "2020-03-18 18:35:42.754124", "modified_by": "Administrator", "module": "Setup", "name": "Sales Person", + "nsm_parent_field": "parent_sales_person", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.json b/erpnext/setup/doctype/supplier_group/supplier_group.json index 5c413341935..b721129b5cd 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.json +++ b/erpnext/setup/doctype/supplier_group/supplier_group.json @@ -1,481 +1,165 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:supplier_group_name", - "beta": 0, "creation": "2013-01-10 16:34:24", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, + "engine": "InnoDB", + "field_order": [ + "supplier_group_name", + "parent_supplier_group", + "is_group", + "section_credit_limit", + "payment_terms", + "default_payable_account", + "accounts", + "lft", + "rgt", + "old_parent" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "supplier_group_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": "Supplier Group Name", - "length": 0, - "no_copy": 0, "oldfieldname": "supplier_type", "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": 1, - "collapsible": 0, - "columns": 0, "fieldname": "parent_supplier_group", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Parent Supplier Group", - "length": 0, - "no_copy": 0, - "options": "Supplier Group", - "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": "Supplier Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "is_group", "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": "Is Group", - "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": "Is Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "section_credit_limit", "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": "Credit Limit", - "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": "Credit Limit" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "payment_terms", "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 Payment Terms Template", - "length": 0, - "no_copy": 0, - "options": "Payment Terms 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": "Payment Terms Template" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "default_payable_account", "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": "Default Payable Account", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Default Payable Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:!doc.__islocal", "description": "Mention if non-standard receivable account applicable", "fieldname": "accounts", "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": "Accounts", - "length": 0, - "no_copy": 0, - "options": "Party Account", - "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": "Party Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "lft", "fieldtype": "Int", "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": "lft", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, "report_hide": 1, - "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": "rgt", "fieldtype": "Int", "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": "rgt", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, "report_hide": 1, - "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": "old_parent", "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Old Parent", - "length": 0, "no_copy": 1, "options": "Supplier Group", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-flag", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-29 06:25:57.589824", + "is_tree": 1, + "links": [], + "modified": "2020-03-18 18:35:48.317675", "modified_by": "Administrator", "module": "Setup", "name": "Supplier Group", + "nsm_parent_field": "parent_supplier_group", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Purchase Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Purchase Manager" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Purchase User" }, { - "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": "Purchase Master Manager", "set_user_permissions": 1, "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": 0, "role": "Purchase Master Manager", - "set_user_permissions": 0, - "share": 0, - "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": 0, - "role": "Purchase Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Purchase Manager" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, - "report": 0, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Purchase User" } ], - "quick_entry": 0, - "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/territory/territory.json b/erpnext/setup/doctype/territory/territory.json index 91a3dda2bb0..3a993d2c746 100644 --- a/erpnext/setup/doctype/territory/territory.json +++ b/erpnext/setup/doctype/territory/territory.json @@ -121,12 +121,14 @@ ], "icon": "fa fa-map-marker", "idx": 1, + "is_tree": 1, "links": [], - "modified": "2020-01-28 13:49:31.905800", + "modified": "2020-03-18 18:35:43.828485", "modified_by": "Administrator", "module": "Setup", "name": "Territory", "name_case": "Title Case", + "nsm_parent_field": "parent_territory", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json index 2bf1ed89838..fad9de515a2 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.json +++ b/erpnext/stock/doctype/warehouse/warehouse.json @@ -228,11 +228,13 @@ ], "icon": "fa fa-building", "idx": 1, + "is_tree": 1, "links": [], - "modified": "2020-01-28 13:50:59.368846", + "modified": "2020-03-18 18:35:42.266202", "modified_by": "Administrator", "module": "Stock", "name": "Warehouse", + "nsm_parent_field": "parent_warehouse", "owner": "Administrator", "permissions": [ { From c2f678265866cf207d43eba5aa8204052208aa8f Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 19 Mar 2020 11:02:34 +0530 Subject: [PATCH 218/929] fix: Unable to submit landed cost voucher (#20979) --- .../stock/doctype/landed_cost_voucher/landed_cost_voucher.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index 7df40fb02cd..d97b699a0f3 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -104,7 +104,6 @@ class LandedCostVoucher(Document): def update_landed_cost(self): for d in self.get("purchase_receipts"): doc = frappe.get_doc(d.receipt_document_type, d.receipt_document) - # check if there are {qty} assets created and linked to this receipt document self.validate_asset_qty_and_status(d.receipt_document_type, doc) @@ -123,6 +122,8 @@ class LandedCostVoucher(Document): # update latest valuation rate in serial no self.update_rate_in_serial_no_for_non_asset_items(doc) + for d in self.get("purchase_receipts"): + doc = frappe.get_doc(d.receipt_document_type, d.receipt_document) # update stock & gl entries for cancelled state of PR doc.docstatus = 2 doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True) From 8c56f8595e21e96a82f574bca46bd252f351e4af Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 19 Mar 2020 11:03:14 +0530 Subject: [PATCH 219/929] fix: Unable to submit landed cost voucher (#20978) --- .../stock/doctype/landed_cost_voucher/landed_cost_voucher.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index 7df40fb02cd..d97b699a0f3 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -104,7 +104,6 @@ class LandedCostVoucher(Document): def update_landed_cost(self): for d in self.get("purchase_receipts"): doc = frappe.get_doc(d.receipt_document_type, d.receipt_document) - # check if there are {qty} assets created and linked to this receipt document self.validate_asset_qty_and_status(d.receipt_document_type, doc) @@ -123,6 +122,8 @@ class LandedCostVoucher(Document): # update latest valuation rate in serial no self.update_rate_in_serial_no_for_non_asset_items(doc) + for d in self.get("purchase_receipts"): + doc = frappe.get_doc(d.receipt_document_type, d.receipt_document) # update stock & gl entries for cancelled state of PR doc.docstatus = 2 doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True) From d1bbda6d717619e766f5c3d2419b771f854af966 Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Thu, 19 Mar 2020 13:04:10 +0550 Subject: [PATCH 220/929] bumped to version 12.6.0 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 23baa838d93..de18e5afbe8 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.5.2' +__version__ = '12.6.0' def get_default_company(user=None): '''Get default company for user''' From 4c4a1aa56da55c6bc435c60795b60a12d5c0fb49 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 19 Mar 2020 13:10:26 +0530 Subject: [PATCH 221/929] fix: allow target warehouses to be changed for work order stock entries --- erpnext/stock/doctype/stock_entry/stock_entry.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 56cb3f06b94..0710e551049 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -294,13 +294,8 @@ class StockEntry(StockController): if validate_for_manufacture: if d.bom_no: d.s_warehouse = None - if not d.t_warehouse: frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx)) - - elif self.pro_doc and (cstr(d.t_warehouse) != self.pro_doc.fg_warehouse and cstr(d.t_warehouse) != self.pro_doc.scrap_warehouse): - frappe.throw(_("Target warehouse in row {0} must be same as Work Order").format(d.idx)) - else: d.t_warehouse = None if not d.s_warehouse: From 794064db9fb135d354aa06d3eeaa7235fb5d9271 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 19 Mar 2020 14:44:16 +0530 Subject: [PATCH 222/929] fix: stock entry qty must be positive --- erpnext/stock/doctype/stock_entry/stock_entry.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 56cb3f06b94..a6da3d306b4 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -177,6 +177,10 @@ class StockEntry(StockController): stock_items = self.get_stock_items() serialized_items = self.get_serialized_items() for item in self.get("items"): + if item.qty and item.qty < 0: + frappe.throw(_("Row {0}: The item {1}, quantity must be positive number") + .format(item.idx, frappe.bold(item.item_code))) + if item.item_code not in stock_items: frappe.throw(_("{0} is not a stock Item").format(item.item_code)) From e53e96f01bcb033b511bf9a2c7ee15fd8c0d878f Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Fri, 20 Mar 2020 13:57:30 +0530 Subject: [PATCH 223/929] chore: pass traceback and error message as kwargs fixes issue where the system tries to generate an error log but fails with the following error: Traceback (most recent call last): File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/setup/doctype/company/company.py", line 421, in install_country_fixtures frappe.get_attr(module_name)(company_doc, False) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/regional/india/setup.py", line 17, in setup make_fixtures(company) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/regional/india/setup.py", line 523, in make_fixtures set_tax_withholding_category(company) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/regional/india/setup.py", line 543, in set_tax_withholding_category fiscal_year = get_fiscal_year(today(), company=company)[0] File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/accounts/utils.py", line 24, in get_fiscal_year return get_fiscal_years(date, fiscal_year, label, verbose, company, as_dict=as_dict)[0] File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/accounts/utils.py", line 80, in get_fiscal_years raise FiscalYearError(error_msg) erpnext.accounts.utils.FiscalYearError: Date 20-03-2020 not in any active Fiscal Year. During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/app.py", line 62, in application response = frappe.api.handle() File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/api.py", line 55, in handle return frappe.handler.handle() File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/handler.py", line 22, in handle data = execute_cmd(cmd) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/handler.py", line 61, in execute_cmd return frappe.call(method, **frappe.form_dict) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/__init__.py", line 1054, in call return fn(*args, **newargs) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/desk/form/save.py", line 22, in savedocs doc.save() File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 273, in save return self._save(*args, **kwargs) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 296, in _save self.insert() File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 260, in insert self.run_post_save_methods() File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 926, in run_post_save_methods self.run_method("on_update") File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 794, in run_method out = Document.hook(fn)(self, *args, **kwargs) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 1065, in composer return composed(self, method, *args, **kwargs) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 1048, in runner add_to_return_value(self, fn(self, *args, **kwargs)) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 788, in fn = lambda self, *args, **kwargs: getattr(self, method)(*args, **kwargs) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/setup/doctype/company/company.py", line 107, in on_update install_country_fixtures(self.name) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/setup/doctype/company/company.py", line 423, in install_country_fixtures frappe.log_error(str(e), frappe.get_traceback()) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/__init__.py", line 1524, in log_error method=title)).insert(ignore_permissions=True) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 231, in insert self._validate() File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 463, in _validate self._validate_length() File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/base_document.py", line 583, in _validate_length self.throw_length_exceeded_error(df, max_length, value) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/base_document.py", line 599, in throw_length_exceeded_error .format(reference, _(df.label), max_length, value), frappe.CharacterLengthExceededError, title=_('Value too big')) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/__init__.py", line 377, in throw msgprint(msg, raise_exception=exc, title=title, indicator='red', is_minimizable=is_minimizable) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/__init__.py", line 356, in msgprint _raise_exception() File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/__init__.py", line 316, in _raise_exception raise raise_exception(msg) frappe.exceptions.CharacterLengthExceededError: Error Log f2cbb8c0f2: 'Title' (Traceback (most recent call last): File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/setup/doctype/company/company.py", line 421, in install_country_fixtures frappe.get_attr(module_name)(company_doc, False) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/regional/india/setup.py", line 17, in setup make_fixtures(company) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/regional/india/setup.py", line 523, in make_fixtures set_tax_withholding_category(company) File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/regional/india/setup.py", line 543, in set_tax_withholding_category fiscal_year = get_fiscal_year(today(), company=company)[0] File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/accounts/utils.py", line 24, in get_fiscal_year return get_fiscal_years(date, fiscal_year, label, verbose, company, as_dict=as_dict)[0] File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/accounts/utils.py", line 80, in get_fiscal_years raise FiscalYearError(error_msg) erpnext.accounts.utils.FiscalYearError: Date 20-03-2020 not in any active Fiscal Year. ) will get truncated, as max characters allowed is 140 Signed-off-by: Chinmay D. Pai --- erpnext/setup/doctype/company/company.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index c3fd3566578..bc27f40f560 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -420,7 +420,7 @@ def install_country_fixtures(company): module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(company_doc.country)) frappe.get_attr(module_name)(company_doc, False) except Exception as e: - frappe.log_error(str(e), frappe.get_traceback()) + frappe.log_error(title=str(e), message=frappe.get_traceback()) frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(company_doc.country))) @@ -590,4 +590,4 @@ def get_default_company_address(name, sort_key='is_primary_address', existing_ad if out: return sorted(out, key = functools.cmp_to_key(lambda x,y: cmp(y[1], x[1])))[0][0] else: - return None \ No newline at end of file + return None From e2bb2593db815aaa78558f7830cb288910d9ae4c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 20 Mar 2020 12:51:16 +0530 Subject: [PATCH 224/929] fix: Add currency in options of Opportunity Amount - To show currect symbol in list view --- erpnext/crm/doctype/opportunity/opportunity.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 66e3ca48dd2..979e4c7a67f 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", @@ -208,7 +209,8 @@ { "fieldname": "opportunity_amount", "fieldtype": "Currency", - "label": "Opportunity Amount" + "label": "Opportunity Amount", + "options": "currency" }, { "default": "0", @@ -412,7 +414,8 @@ ], "icon": "fa fa-info-sign", "idx": 195, - "modified": "2019-09-30 12:58:37.385400", + "links": [], + "modified": "2020-03-20 12:28:45.228994", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From 93e72fdd219d6d35198f1771feb84d7e64c8b780 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 20 Mar 2020 17:49:59 +0530 Subject: [PATCH 225/929] fix: UOM fixes in Sales Order,Material Request & Production Plan --- .../doctype/production_plan/production_plan.py | 2 +- .../selling/doctype/sales_order/sales_order.py | 16 +++++----------- .../doctype/material_request/material_request.py | 2 +- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 76202e5a6f3..61b4ca838b3 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -144,7 +144,7 @@ class ProductionPlan(Document): item_condition = " and mr_item.item_code ={0}".format(frappe.db.escape(self.item_code)) items = frappe.db.sql("""select distinct parent, name, item_code, warehouse, description, - (qty - ordered_qty) as pending_qty + (qty - ordered_qty) * conversion_factor as pending_qty from `tabMaterial Request Item` mr_item where parent in (%s) and docstatus = 1 and qty > ordered_qty and exists (select name from `tabBOM` bom where bom.item=mr_item.item_code diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 9e4a71d60e5..19cf65dee86 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -500,7 +500,7 @@ def close_or_unclose_sales_orders(names, status): def get_requested_item_qty(sales_order): return frappe._dict(frappe.db.sql(""" - select sales_order_item, sum(stock_qty) + select sales_order_item, sum(qty) from `tabMaterial Request Item` where docstatus = 1 and sales_order = %s @@ -511,16 +511,12 @@ def get_requested_item_qty(sales_order): 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): # qty is for packed items, because packed items don't have stock_qty field - qty = source.get("stock_qty") or source.get("qty") + qty = source.get("qty") target.project = source_parent.project target.qty = qty - requested_item_qty.get(source.name, 0) - target.conversion_factor = 1 - target.stock_qty = qty - requested_item_qty.get(source.name, 0) + target.stock_qty = flt(target.qty) * flt(target.conversion_factor) doc = get_mapped_doc("Sales Order", source_name, { "Sales Order": { @@ -541,14 +537,12 @@ def make_material_request(source_name, target_doc=None): "doctype": "Material Request Item", "field_map": { "name": "sales_order_item", - "parent": "sales_order", - "stock_uom": "uom", - "stock_qty": "qty" + "parent": "sales_order" }, "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) + }, target_doc) return doc diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 6531e095b84..78383e05a6f 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -484,7 +484,7 @@ def raise_work_orders(material_request): wo_order = frappe.new_doc("Work Order") wo_order.update({ "production_item": d.item_code, - "qty": d.qty - d.ordered_qty, + "qty": d.stock_qty - d.ordered_qty, "fg_warehouse": d.warehouse, "wip_warehouse": default_wip_warehouse, "description": d.description, From 20ab2c1e20a48a08bf32de9324ae45af008c148d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 22 Mar 2020 21:50:22 +0530 Subject: [PATCH 226/929] fix: Remove quick entry from accouting dimensions --- .../accounting_dimension.js | 6 ------ .../accounting_dimension.json | 17 +++++++++++++++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js index a36f4214d7c..3c12f85f938 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js @@ -48,12 +48,6 @@ frappe.ui.form.on('Accounting Dimension', { frm.set_value('label', frm.doc.document_type); frm.set_value('fieldname', frappe.model.scrub(frm.doc.document_type)); - if (frm.is_new()){ - let row = frappe.model.add_child(frm.doc, "Accounting Dimension Detail", "dimension_defaults"); - row.reference_document = frm.doc.document_type; - frm.refresh_fields("dimension_defaults"); - } - frappe.db.get_value('Accounting Dimension', {'document_type': frm.doc.document_type}, 'document_type', (r) => { if (r && r.document_type) { frm.set_df_property('document_type', 'description', "Document type is already set as dimension"); diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json index cf6dc7a8fa5..cf55d554fb3 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "field:label", "creation": "2019-05-04 18:13:37.002352", "doctype": "DocType", @@ -46,7 +47,8 @@ "options": "Accounting Dimension Detail" } ], - "modified": "2019-07-17 16:49:31.134385", + "links": [], + "modified": "2020-03-22 20:34:39.805728", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting Dimension", @@ -63,9 +65,20 @@ "role": "System Manager", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 } ], - "quick_entry": 1, "sort_field": "modified", "sort_order": "ASC", "track_changes": 1 From b05922026d6da9fa1bc6b421809641730400da89 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 23 Mar 2020 16:31:39 +0530 Subject: [PATCH 227/929] feat: import supplier invoices --- erpnext/patches.txt | 1 + .../v12_0/set_permission_einvoicing.py | 15 + .../import_supplier_invoice/__init__.py | 0 .../import_supplier_invoice.js | 46 ++ .../import_supplier_invoice.json | 105 +++++ .../import_supplier_invoice.py | 402 ++++++++++++++++++ .../test_import_supplier_invoice.py | 10 + erpnext/regional/italy/setup.py | 25 ++ erpnext/regional/italy/utils.py | 2 + .../purchase_einvoice/__init__.py | 0 .../purchase_einvoice/purchase_einvoice.json | 23 + 11 files changed, 629 insertions(+) create mode 100644 erpnext/patches/v12_0/set_permission_einvoicing.py create mode 100644 erpnext/regional/doctype/import_supplier_invoice/__init__.py create mode 100644 erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js create mode 100644 erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json create mode 100644 erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py create mode 100644 erpnext/regional/doctype/import_supplier_invoice/test_import_supplier_invoice.py create mode 100644 erpnext/regional/print_format/purchase_einvoice/__init__.py create mode 100644 erpnext/regional/print_format/purchase_einvoice/purchase_einvoice.json diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4e72d5ecd6b..de4c89c5ace 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -650,3 +650,4 @@ erpnext.patches.v12_0.update_price_or_product_discount erpnext.patches.v12_0.add_export_type_field_in_party_master erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22 erpnext.patches.v12_0.create_irs_1099_field_united_states +erpnext.patches.v12_0.set_permission_einvoicing diff --git a/erpnext/patches/v12_0/set_permission_einvoicing.py b/erpnext/patches/v12_0/set_permission_einvoicing.py new file mode 100644 index 00000000000..1095c8c43e7 --- /dev/null +++ b/erpnext/patches/v12_0/set_permission_einvoicing.py @@ -0,0 +1,15 @@ +import frappe +from erpnext.regional.italy.setup import make_custom_fields +from frappe.permissions import add_permission, update_permission_property + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'Italy'}) + + if not company: + return + + make_custom_fields() + + add_permission('Import Supplier Invoice', 'Accounts Manager', 0) + update_permission_property('Import Supplier Invoice', 'Accounts Manager', 0, 'write', 1) + update_permission_property('Import Supplier Invoice', 'Accounts Manager', 0, 'create', 1) \ No newline at end of file diff --git a/erpnext/regional/doctype/import_supplier_invoice/__init__.py b/erpnext/regional/doctype/import_supplier_invoice/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js new file mode 100644 index 00000000000..c2d6edfc773 --- /dev/null +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js @@ -0,0 +1,46 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.ui.form.on('Import Supplier Invoice', { + onload: function(frm) { + frappe.realtime.on("import_invoice_update", function (data) { + frm.dashboard.show_progress(data.title, (data.count / data.total) * 100, data.message); + if (data.count == data.total) { + window.setTimeout(title => frm.dashboard.hide_progress(title), 1500, data.title); + } + }); + }, + setup: function(frm) { + frm.set_query("tax_account", function(doc) { + return { + filters: { + account_type: 'Tax', + company: doc.company + } + }; + }); + + frm.set_query("default_buying_price_list", function(doc) { + return { + filters: { + currency: frappe.get_doc(":Company", doc.company).default_currency + } + }; + }); + }, + + refresh: function(frm) { + frm.trigger("toggle_read_only_fields"); + }, + + toggle_read_only_fields: function(frm) { + if (in_list(["File Import Completed", "Processing File Data"], frm.doc.status)) { + cur_frm.set_read_only(); + cur_frm.refresh_fields(); + frm.set_df_property("import_invoices", "hidden", 1); + } else { + frm.set_df_property("import_invoices", "hidden", 0); + } + } + +}); \ No newline at end of file diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json new file mode 100644 index 00000000000..59e955c23f4 --- /dev/null +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json @@ -0,0 +1,105 @@ +{ + "actions": [], + "creation": "2019-10-15 12:33:21.845329", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "invoice_series", + "company", + "item_code", + "column_break_5", + "supplier_group", + "tax_account", + "default_buying_price_list", + "upload_xml_invoices_section", + "zip_file", + "import_invoices", + "status" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item", + "reqd": 1 + }, + { + "fieldname": "supplier_group", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Supplier Group", + "options": "Supplier Group", + "reqd": 1 + }, + { + "fieldname": "tax_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Tax Account", + "options": "Account", + "reqd": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "zip_file", + "fieldtype": "Attach", + "label": "Zip File" + }, + { + "description": "Click on Import Invoices button once the zip file has been attached to the document. Any errors related to processing will be shown in the Error Log.", + "fieldname": "import_invoices", + "fieldtype": "Button", + "label": "Import Invoices", + "options": "process_file_data" + }, + { + "fieldname": "status", + "fieldtype": "Data", + "label": "Status", + "read_only": 1 + }, + { + "fieldname": "invoice_series", + "fieldtype": "Select", + "label": "Invoice Series", + "options": "ACC-PINV-.YYYY.-", + "reqd": 1 + }, + { + "fieldname": "default_buying_price_list", + "fieldtype": "Link", + "label": "Default Buying Price List", + "options": "Price List", + "reqd": 1 + }, + { + "fieldname": "upload_xml_invoices_section", + "fieldtype": "Section Break", + "label": "Upload XML Invoices" + } + ], + "links": [], + "modified": "2019-12-10 16:37:26.793398", + "modified_by": "Administrator", + "module": "Regional", + "name": "Import Supplier Invoice", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py new file mode 100644 index 00000000000..72fe17fb379 --- /dev/null +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py @@ -0,0 +1,402 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +from decimal import Decimal +import json +import re +import traceback +import zipfile +import frappe, erpnext +from frappe import _ +from frappe.model.document import Document +from frappe.custom.doctype.custom_field.custom_field import create_custom_field +from frappe.utils.data import format_datetime +from bs4 import BeautifulSoup as bs +from frappe.utils import cint, flt, today, nowdate, add_days, get_files_path, get_datetime_str +import dateutil +from frappe.utils.file_manager import save_file + +class ImportSupplierInvoice(Document): + def validate(self): + if not frappe.db.get_value("Stock Settings", fieldname="stock_uom"): + frappe.throw(_("Please set default UOM in Stock Settings")) + + def autoname(self): + if not self.name: + self.name = "Import Invoice on " + format_datetime(self.creation) + + def import_xml_data(self): + import_file = frappe.get_doc("File", {"file_url": self.zip_file}) + self.publish("File Import", _("Processing XML Files"), 1, 3) + + self.file_count = 0 + self.purchase_invoices_count = 0 + self.default_uom = frappe.db.get_value("Stock Settings", fieldname="stock_uom") + + with zipfile.ZipFile(get_full_path(self.zip_file)) as zf: + for file_name in zf.namelist(): + content = get_file_content(file_name, zf) + file_content = bs(content, "xml") + self.prepare_data_for_import(file_content, file_name, content) + + if self.purchase_invoices_count == self.file_count: + self.status = "File Import Completed" + self.publish("File Import", _("XML Files Processed"), 2, 3) + else: + self.status = "Partially Completed - Check Error Log" + self.publish("File Import", _("XML Files Processed"), 2, 3) + + self.save() + self.publish("File Import", _("XML Files Processed"), 3, 3) + + def prepare_data_for_import(self, file_content, file_name, encoded_content): + for line in file_content.find_all("DatiGeneraliDocumento"): + invoices_args = { + "company": self.company, + "naming_series": self.invoice_series, + "document_type": line.TipoDocumento.text, + "bill_date": get_datetime_str(line.Data.text), + "invoice_no": line.Numero.text, + "total_discount": 0, + "items": [], + "buying_price_list": self.default_buying_price_list + } + + if not invoices_args.get("invoice_no", ''): return + + supp_dict = get_supplier_details(file_content) + invoices_args["destination_code"] = get_destination_code_from_file(file_content) + self.prepare_items_for_invoice(file_content, invoices_args) + invoices_args["taxes"] = get_taxes_from_file(file_content, self.tax_account) + invoices_args["terms"] = get_payment_terms_from_file(file_content) + + supplier_name = create_supplier(self.supplier_group, supp_dict) + address = create_address(supplier_name, supp_dict) + pi_name = create_purchase_invoice(supplier_name, file_name, invoices_args, self.name) + + self.file_count += 1 + if pi_name: + self.purchase_invoices_count += 1 + file_save = save_file(file_name, encoded_content, "Purchase Invoice", + pi_name, folder=None, decode=False, is_private=0, df=None) + + def prepare_items_for_invoice(self, file_content, invoices_args): + qty = 1 + rate, tax_rate = [0 ,0] + uom = self.default_uom + + #read file for item information + for line in file_content.find_all("DettaglioLinee"): + if line.find("PrezzoUnitario") and line.find("PrezzoTotale"): + rate = flt(line.PrezzoUnitario.text) or 0 + line_total = flt(line.PrezzoTotale.text) or 0 + + if rate and flt(line_total) / rate != 1.0 and line.find("Quantita"): + qty = flt(line.Quantita.text) or 0 + if line.find("UnitaMisura"): + uom = create_uom(line.UnitaMisura.text) + + if (rate < 0 and line_total < 0): + qty *= -1 + invoices_args["return_invoice"] = 1 + + if line.find("AliquotaIVA"): + tax_rate = flt(line.AliquotaIVA.text) + + line_str = re.sub('[^A-Za-z0-9]+', '-', line.Descrizione.text) + item_name = line_str[0:140] + + invoices_args['items'].append({ + "item_code": self.item_code, + "item_name": item_name, + "description": line_str, + "qty": qty, + "uom": uom, + "rate": abs(rate), + "conversion_factor": 1.0, + "tax_rate": tax_rate + }) + + for disc_line in line.find_all("ScontoMaggiorazione"): + if disc_line.find("Percentuale"): + invoices_args["total_discount"] += flt((flt(disc_line.Percentuale.text) / 100) * (rate * qty)) + + def process_file_data(self): + self.status = "Processing File Data" + self.save() + frappe.enqueue_doc(self.doctype, self.name, "import_xml_data", queue="long", timeout=3600) + + def publish(self, title, message, count, total): + frappe.publish_realtime("import_invoice_update", {"title": title, "message": message, "count": count, "total": total}) + +def get_file_content(file_name, zip_file_object): + content = '' + encoded_content = zip_file_object.read(file_name) + + try: + content = encoded_content.decode("utf-8-sig") + except UnicodeDecodeError: + try: + content = encoded_content.decode("utf-16") + except UnicodeDecodeError as e: + frappe.log_error(message=e, title="UTF-16 encoding error for File Name: " + file_name) + + return content + +def get_supplier_details(file_content): + supplier_info = {} + for line in file_content.find_all("CedentePrestatore"): + supplier_info['tax_id'] = line.DatiAnagrafici.IdPaese.text + line.DatiAnagrafici.IdCodice.text + if line.find("CodiceFiscale"): + supplier_info['fiscal_code'] = line.DatiAnagrafici.CodiceFiscale.text + + if line.find("RegimeFiscale"): + supplier_info['fiscal_regime'] = line.DatiAnagrafici.RegimeFiscale.text + + if line.find("Denominazione"): + supplier_info['supplier'] = line.DatiAnagrafici.Anagrafica.Denominazione.text + + if line.find("Nome"): + supplier_info['supplier'] = (line.DatiAnagrafici.Anagrafica.Nome.text + + " " + line.DatiAnagrafici.Anagrafica.Cognome.text) + + supplier_info['address_line1'] = line.Sede.Indirizzo.text + supplier_info['city'] = line.Sede.Comune.text + if line.find("Provincia"): + supplier_info['province'] = line.Sede.Provincia.text + + supplier_info['pin_code'] = line.Sede.CAP.text + supplier_info['country'] = get_country(line.Sede.Nazione.text) + + return supplier_info + +def get_taxes_from_file(file_content, tax_account): + taxes = [] + #read file for taxes information + for line in file_content.find_all("DatiRiepilogo"): + if line.find("AliquotaIVA"): + if line.find("EsigibilitaIVA"): + descr = line.EsigibilitaIVA.text + else: + descr = "None" + taxes.append({ + "charge_type": "Actual", + "account_head": tax_account, + "tax_rate": flt(line.AliquotaIVA.text) or 0, + "description": descr, + "tax_amount": flt(line.Imposta.text) if len(line.find("Imposta"))!=0 else 0 + }) + + return taxes + +def get_payment_terms_from_file(file_content): + terms = [] + #Get mode of payment dict from setup + mop_options = frappe.get_meta('Mode of Payment').fields[4].options + mop_str = re.sub('\n', ',', mop_options) + mop_dict = dict(item.split("-") for item in mop_str.split(",")) + #read file for payment information + for line in file_content.find_all("DettaglioPagamento"): + mop_code = line.ModalitaPagamento.text + '-' + mop_dict.get(line.ModalitaPagamento.text) + if line.find("DataScadenzaPagamento"): + due_date = dateutil.parser.parse(line.DataScadenzaPagamento.text).strftime("%Y-%m-%d") + else: + due_date = today() + terms.append({ + "mode_of_payment_code": mop_code, + "bank_account_iban": line.IBAN.text if line.find("IBAN") else "", + "due_date": due_date, + "payment_amount": line.ImportoPagamento.text + }) + + return terms + +def get_destination_code_from_file(file_content): + destination_code = '' + for line in file_content.find_all("DatiTrasmissione"): + destination_code = line.CodiceDestinatario.text + + return destination_code + +def create_supplier(supplier_group, args): + args = frappe._dict(args) + + existing_supplier_name = frappe.db.get_value("Supplier", + filters={"tax_id": args.tax_id}, fieldname="name") + if existing_supplier_name: + pass + else: + existing_supplier_name = frappe.db.get_value("Supplier", + filters={"name": args.supplier}, fieldname="name") + + if existing_supplier_name: + filters = [ + ["Dynamic Link", "link_doctype", "=", "Supplier"], + ["Dynamic Link", "link_name", "=", args.existing_supplier_name], + ["Dynamic Link", "parenttype", "=", "Contact"] + ] + + if not frappe.get_list("Contact", filters): + new_contact = frappe.new_doc("Contact") + new_contact.first_name = args.supplier + new_contact.append('links', { + "link_doctype": "Supplier", + "link_name": existing_supplier_name + }) + new_contact.insert(ignore_mandatory=True) + + return existing_supplier_name + else: + + new_supplier = frappe.new_doc("Supplier") + new_supplier.supplier_name = args.supplier + new_supplier.supplier_group = supplier_group + new_supplier.tax_id = args.tax_id + new_supplier.fiscal_code = args.fiscal_code + new_supplier.fiscal_regime = args.fiscal_regime + new_supplier.save() + + new_contact = frappe.new_doc("Contact") + new_contact.first_name = args.supplier + new_contact.append('links', { + "link_doctype": "Supplier", + "link_name": new_supplier.name + }) + + new_contact.insert(ignore_mandatory=True) + + return new_supplier.name + +def create_address(supplier_name, args): + args = frappe._dict(args) + + filters = [ + ["Dynamic Link", "link_doctype", "=", "Supplier"], + ["Dynamic Link", "link_name", "=", supplier_name], + ["Dynamic Link", "parenttype", "=", "Address"] + ] + + existing_address = frappe.get_list("Address", filters) + + if args.address_line1: + new_address_doc = frappe.new_doc("Address") + new_address_doc.address_line1 = args.address_line1 + + if args.city: + new_address_doc.city = args.city + else: + new_address_doc.city = "Not Provided" + + for field in ["province", "pincode", "country"]: + if args.get(field): + new_address_doc.set(field, args.get(field)) + + for address in existing_address: + address_doc = frappe.get_doc("Address", address["name"]) + if (address_doc.address_line1 == new_address_doc.address_line1 and + address_doc.pincode == new_address_doc.pincode): + return address + + new_address_doc.append("links", { + "link_doctype": "Supplier", + "link_name": supplier_name + }) + new_address_doc.address_type = "Billing" + new_address_doc.insert(ignore_mandatory=True) + return new_address_doc.name + else: + return None + +def create_purchase_invoice(supplier_name, file_name, args, name): + args = frappe._dict(args) + pi = frappe.get_doc({ + "doctype": "Purchase Invoice", + "company": args.company, + "currency": erpnext.get_company_currency(args.company), + "naming_series": args.naming_series, + "supplier": supplier_name, + "is_return": args.is_return, + "posting_date": today(), + "bill_no": args.bill_no, + "buying_price_list": args.buying_price_list, + "bill_date": args.bill_date, + "destination_code": args.destination_code, + "document_type": args.document_type, + "disable_rounded_total": 1, + "items": args["items"], + "taxes": args["taxes"] + }) + + try: + pi.set_missing_values() + pi.insert(ignore_mandatory=True) + + #if discount exists in file, apply any discount on grand total + if args.total_discount > 0: + pi.apply_discount_on = "Grand Total" + pi.discount_amount = args.total_discount + pi.save() + #adjust payment amount to match with grand total calculated + calc_total = 0 + adj = 0 + for term in args.terms: + calc_total += flt(term["payment_amount"]) + if flt(calc_total - flt(pi.grand_total)) != 0: + adj = calc_total - flt(pi.grand_total) + pi.payment_schedule = [] + for term in args.terms: + pi.append('payment_schedule',{"mode_of_payment_code": term["mode_of_payment_code"], + "bank_account_iban": term["bank_account_iban"], + "due_date": term["due_date"], + "payment_amount": flt(term["payment_amount"]) - adj }) + adj = 0 + pi.imported_grand_total = calc_total + pi.save() + return pi.name + except Exception as e: + frappe.db.set_value("Import Supplier Invoice", name, "status", "Error") + frappe.log_error(message=e, + title="Create Purchase Invoice: " + args.get("bill_no") + "File Name: " + file_name) + return None + +def get_country(code): + existing_country_name = frappe.db.get_value("Country", + filters={"code": code}, fieldname="name") + if existing_country_name: + return existing_country_name + else: + frappe.throw(_("Country Code in File does not match with country code set up in the system")) + +def create_uom(uom): + existing_uom = frappe.db.get_value("UOM", + filters={"uom_name": uom}, fieldname="uom_name") + if existing_uom: + return existing_uom + else: + new_uom = frappe.new_doc("UOM") + new_uom.uom_name = uom + new_uom.save() + return new_uom.uom_name + +def get_full_path(file_name): + """Returns file path from given file name""" + file_path = file_name + + if "/" not in file_path: + file_path = "/files/" + file_path + + if file_path.startswith("/private/files/"): + file_path = get_files_path(*file_path.split("/private/files/", 1)[1].split("/"), is_private=1) + + elif file_path.startswith("/files/"): + file_path = get_files_path(*file_path.split("/files/", 1)[1].split("/")) + + elif file_path.startswith("http"): + pass + + elif not self.file_url: + frappe.throw(_("There is some problem with the file url: {0}").format(file_path)) + + return file_path \ No newline at end of file diff --git a/erpnext/regional/doctype/import_supplier_invoice/test_import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/test_import_supplier_invoice.py new file mode 100644 index 00000000000..d1caf77fc2c --- /dev/null +++ b/erpnext/regional/doctype/import_supplier_invoice/test_import_supplier_invoice.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestImportSupplierInvoice(unittest.TestCase): + pass diff --git a/erpnext/regional/italy/setup.py b/erpnext/regional/italy/setup.py index 1526d6f62f0..2d0ad66b0a0 100644 --- a/erpnext/regional/italy/setup.py +++ b/erpnext/regional/italy/setup.py @@ -155,6 +155,31 @@ def make_custom_fields(update=True): fetch_from="country.code"), dict(fieldname='state_code', label='State Code', fieldtype='Data', insert_after='state', print_hide=1) + ], + 'Purchase Invoice': [ + dict(fieldname='document_type', label='Document Type', + fieldtype='Data', insert_after='company', print_hide=1, read_only=1 + ), + dict(fieldname='destination_code', label='Destination Code', + fieldtype='Data', insert_after='company', print_hide=1, read_only=1 + ), + dict(fieldname='imported_grand_total', label='Imported Grand Total', + fieldtype='Data', insert_after='update_auto_repeat_reference', print_hide=1, read_only=1 + ) + ], + 'Purchase Taxes and Charges': [ + dict(fieldname='tax_rate', label='Tax Rate', + fieldtype='Data', insert_after='parenttype', print_hide=1, read_only=0 + ) + ], + 'Supplier': [ + dict(fieldname='fiscal_code', label='Fiscal Code', + fieldtype='Data', insert_after='tax_id', print_hide=1, read_only=1 + ), + dict(fieldname='fiscal_regime', label='Fiscal Regime', + fieldtype='Select', insert_after='fiscal_code', print_hide=1, read_only=1, + options= "\nRF01\nRF02\nRF04\nRF05\nRF06\nRF07\nRF08\nRF09\nRF10\nRF11\nRF12\nRF13\nRF14\nRF15\nRF16\nRF17\nRF18\nRF19" + ) ] } diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py index bc8d00d8b8b..2af72f8e276 100644 --- a/erpnext/regional/italy/utils.py +++ b/erpnext/regional/italy/utils.py @@ -13,6 +13,8 @@ from erpnext.regional.italy import state_codes def update_itemised_tax_data(doc): if not doc.taxes: return + if doc.doctype == "Purchase Invoice": return + itemised_tax = get_itemised_tax(doc.taxes) for row in doc.items: diff --git a/erpnext/regional/print_format/purchase_einvoice/__init__.py b/erpnext/regional/print_format/purchase_einvoice/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/regional/print_format/purchase_einvoice/purchase_einvoice.json b/erpnext/regional/print_format/purchase_einvoice/purchase_einvoice.json new file mode 100644 index 00000000000..88f31dd1308 --- /dev/null +++ b/erpnext/regional/print_format/purchase_einvoice/purchase_einvoice.json @@ -0,0 +1,23 @@ +{ + "align_labels_right": 0, + "creation": "2019-10-16 00:47:08.877767", + "custom_format": 0, + "disabled": 1, + "doc_type": "Purchase Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"
\\t\\t\\t\\t

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

\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
\\nCedente/prestatore (fornitore)\\n\\nCessionario/committente (cliente)\\n
\\n

Identificstivo fiscale ai fini IVA: {{frappe.db.get_value(\\\"Supplier\\\", doc.supplier, \\\"tax_id\\\")}}

\\n

Codice fiscale: {{frappe.db.get_value(\\\"Supplier\\\", doc.supplier, \\\"fiscal_code\\\")}}

\\n

Denominazione: {{frappe.db.get_value(\\\"Supplier\\\", doc.supplier, \\\"supplier_name\\\")}}

\\n

Regime fiscale: {{frappe.db.get_value(\\\"Supplier\\\", doc.supplier, \\\"fiscal_regime\\\")}}

\\n

Indrizo: {{frappe.db.get_value(\\\"Address\\\", doc.supplier_address, \\\"address_line1\\\")}}

\\n

Commune: {{frappe.db.get_value(\\\"Address\\\", doc.supplier_address, \\\"city\\\")}} Provincia: {{frappe.db.get_value(\\\"Address\\\", doc.supplier_address, \\\"state_code\\\")}}

\\n

Cap: {{(frappe.db.get_value(\\\"Address\\\", doc.supplier_address, \\\"pincode\\\")) or \\\" \\\"}} Nazione: {{frappe.db.get_value(\\\"Address\\\", doc.supplier_address, \\\"country\\\")}}

\\n
\\n

Identificstivo fiscale ai fini IVA: {{frappe.db.get_value(\\\"Company\\\", doc.company, \\\"tax_id\\\")}}

\\n

Codice fiscale: {{frappe.db.get_value(\\\"Company\\\", doc.company, \\\"fiscal_code\\\")}}

\\n

Denominazione: {{doc.company}}

\\n

Indrizo: {{frappe.db.get_value(\\\"Address\\\", doc.shipping_address, \\\"address_line1\\\")}}

\\n

Commune: {{frappe.db.get_value(\\\"Address\\\", doc.shipping_address, \\\"city\\\")}} Provincia: {{frappe.db.get_value(\\\"Address\\\", doc.shipping_address, \\\"state_code\\\")}}

\\n

Cap: {{frappe.db.get_value(\\\"Address\\\", doc.shipping_address, \\\"pincode\\\")}} Nazione: {{frappe.db.get_value(\\\"Address\\\", doc.shipping_address, \\\"country\\\")}}

\\n
\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
\\nTipologla\\n\\nArt. 73\\n\\nNumero documento\\n\\nData documento\\n\\nCodice destinatario\\n
\\n{{doc.document_type or \\\" \\\"}}\\n\\n{{\\\" \\\"}}\\n\\n{{doc.bill_no or \\\" \\\"}}\\n\\n{{doc.get_formatted(\\\"bill_date\\\") or \\\" \\\"}}\\n\\n{{doc.destination_code or \\\" \\\"}}\\n
\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n{%- for row in doc.items -%}\\n\\n\\n\\n\\n\\n\\n\\n{%- endfor -%}\\n\\n
\\nDescrizione\\n\\nQuantita\\n\\nPrezzo unitario\\n\\nUM\\n\\n%IVA\\n\\nPrezzo totale\\n
\\n{{row.description or \\\" \\\"}}\\n\\n{{row.get_formatted(\\\"qty\\\", doc)}}\\n\\n{{row.get_formatted(\\\"rate\\\", doc)}}\\n\\n{{row.get_formatted(\\\"uom\\\", doc)}}\\n\\n{{row.get_formatted(\\\"tax_rate\\\", doc)}}\\n\\n{{row.get_formatted(\\\"amount\\\", doc)}}\\n
\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n{%- for row in doc.taxes -%}\\n\\n\\n\\n\\n\\n\\n\\n{%- endfor -%}\\n\\n
\\nesigibilita immediata / riferimenti normativi\\n\\n%IVA\\n\\nSpese accessorie\\n\\nArr.\\n\\nTotale imponibile\\n\\nTotale Imposta\\n
\\n{% if 'None' in row.description %}\\n {{ \\\" \\\" }}\\n{% else %}\\n{{row.description}}\\n{% endif %}\\n\\n{{row.get_formatted(\\\"tax_rate\\\", doc)}}\\n\\n{{\\\"0,00\\\"}}\\n\\n{{\\\" \\\"}}\\n\\n{{doc.get_formatted(\\\"base_net_total\\\")}}\\n\\n{{row.get_formatted(\\\"tax_amount\\\", doc)}}\\n
\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
\\nImporto bolio\\n\\nSconto/Magglorazione\\n\\nArr.\\n\\nTotale documento\\n
\\n{{\\\" \\\"}}\\n\\n{{\\\" \\\"}}\\n\\n{{\\\" \\\"}}\\n\\n{{doc.get_formatted(\\\"base_grand_total\\\")}}\\n
\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n{%- for row in doc.payment_schedule -%}\\n\\n\\n\\n\\n\\n\\n{%- endfor -%}\\n\\n
\\nModalita pagamento\\n\\nIBAN\\n\\nInstituto\\n\\nData scadenza\\n\\nImporto\\n
\\n{{row.get_formatted(\\\"mode_of_payment_code\\\",doc)}}\\n\\n{{row.get_formatted(\\\"bank_account_iban\\\",doc)}}\\n\\n{{\\\" \\\"}}\\n\\n{{row.get_formatted(\\\"due_date\\\",doc)}}\\n\\n{{row.get_formatted(\\\"payment_amount\\\",doc)}}\\n
\"}]", + "idx": 0, + "line_breaks": 0, + "modified": "2019-10-16 23:32:37.709344", + "modified_by": "Administrator", + "module": "Regional", + "name": "Purchase eInvoice", + "owner": "Administrator", + "print_format_builder": 1, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file From 243283eec875fa7c4f57caa2615c0c466d1cf312 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 24 Mar 2020 11:34:44 +0530 Subject: [PATCH 228/929] fix: batch selection popup not coming for stock entry --- erpnext/public/js/utils/serial_no_batch_selector.js | 6 +++++- erpnext/stock/doctype/stock_entry/stock_entry.js | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 23895ee4c02..101ac9cd610 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -313,11 +313,15 @@ erpnext.SerialNoBatchSelector = Class.extend({ frappe.throw(__(`Batch ${val} already selected.`)); return; } + + let batch_number = me.item.batch_no || + this.grid_row.on_grid_fields_dict.batch_no.get_value(); + if (me.warehouse_details.name) { frappe.call({ method: 'erpnext.stock.doctype.batch.batch.get_batch_qty', args: { - batch_no: me.item.batch_no, + batch_no: batch_number, warehouse: me.warehouse_details.name, item_code: me.item_code }, diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 92cc8f65047..23ee9f472b8 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -60,7 +60,8 @@ frappe.ui.form.on('Stock Entry', { } } - if(item.s_warehouse) filters["warehouse"] = item.s_warehouse; + filters["warehouse"] = item.s_warehouse || item.t_warehouse; + return { query : "erpnext.controllers.queries.get_batch_no", filters: filters @@ -886,7 +887,7 @@ erpnext.stock.select_batch_and_serial_no = (frm, item) => { } } - if(item && !item.has_serial_no && item.has_batch_no) return; + if(item && !item.has_serial_no && !item.has_batch_no) return; if (frm.doc.purpose === 'Material Receipt') return; frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() { From 87b3dbb2146da8061b8babea76036eb5e4566f30 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 2 Dec 2019 09:37:09 +0530 Subject: [PATCH 229/929] fix: Manufacturing UX, added calendar view for job card --- erpnext/manufacturing/doctype/bom/bom.js | 5 - .../doctype/bom/bom_dashboard.py | 4 +- .../doctype/job_card/job_card.js | 141 +- .../doctype/job_card/job_card.json | 1234 +++---------- .../doctype/job_card/job_card.py | 55 +- .../doctype/job_card/job_card_calendar.js | 21 + .../job_card_time_log/job_card_time_log.json | 247 +-- .../production_plan/production_plan.js | 3 +- .../production_plan/production_plan.json | 1618 +++-------------- .../production_plan_dashboard.py | 2 +- .../doctype/work_order/work_order.js | 178 +- .../doctype/work_order/work_order.json | 4 +- .../doctype/work_order/work_order.py | 38 +- .../doctype/work_order/work_order_calendar.js | 2 + .../work_order/work_order_dashboard.py | 3 +- .../work_order_operation.json | 9 +- 16 files changed, 918 insertions(+), 2646 deletions(-) create mode 100644 erpnext/manufacturing/doctype/job_card/job_card_calendar.js diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 8283fd7e6fc..3acaee4ffbd 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -6,7 +6,6 @@ frappe.provide("erpnext.bom"); frappe.ui.form.on("BOM", { setup: function(frm) { frm.custom_make_buttons = { - 'BOM': 'Duplicate BOM', 'Work Order': 'Work Order', 'Quality Inspection': 'Quality Inspection' }; @@ -91,10 +90,6 @@ frappe.ui.form.on("BOM", { } if(frm.doc.docstatus!=0) { - frm.add_custom_button(__("Duplicate BOM"), function() { - frm.copy_doc(); - }, __("Create")); - frm.add_custom_button(__("Work Order"), function() { frm.trigger("make_work_order"); }, __("Create")); diff --git a/erpnext/manufacturing/doctype/bom/bom_dashboard.py b/erpnext/manufacturing/doctype/bom/bom_dashboard.py index 060cd53ef19..1571125bb06 100644 --- a/erpnext/manufacturing/doctype/bom/bom_dashboard.py +++ b/erpnext/manufacturing/doctype/bom/bom_dashboard.py @@ -23,7 +23,5 @@ def get_data(): 'label': _('Subcontract'), 'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] } - ], - 'disable_create_buttons': ["Item", "Purchase Order", "Purchase Receipt", - "Purchase Invoice", "Job Card", "Stock Entry"] + ] } diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index 1d9544d34f4..6db8f2f5b38 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -7,6 +7,8 @@ frappe.ui.form.on('Job Card', { if(frm.doc.docstatus == 0) { frm.set_df_property("operation", "read_only", frm.doc.operation_id ? 1 : 0); } + frappe.flags.pause_job = 0; + frappe.flags.resume_job = 0; if(!frm.doc.__islocal && frm.doc.items && frm.doc.items.length) { if (frm.doc.for_quantity != frm.doc.transferred_qty) { @@ -18,44 +20,99 @@ frappe.ui.form.on('Job Card', { if (frm.doc.for_quantity != frm.doc.transferred_qty) { frm.add_custom_button(__("Material Transfer"), () => { frm.trigger("make_stock_entry"); - }); + }).addClass("btn-primary"); } } - if (frm.doc.docstatus == 0) { - frm.trigger("make_dashboard"); + if (frm.doc.docstatus == 0 && frm.doc.for_quantity > frm.doc.total_completed_qty + && (!frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) { + frm.trigger("prepare_timer_buttons"); + } + }, - if (!frm.doc.job_started) { - frm.add_custom_button(__("Start Job"), () => { - let row = frappe.model.add_child(frm.doc, 'Job Card Time Log', 'time_logs'); - row.from_time = frappe.datetime.now_datetime(); - frm.set_value('job_started', 1); - frm.set_value('started_time' , row.from_time); - frm.save(); - }); - } else { - frm.add_custom_button(__("Complete Job"), () => { - let completed_time = frappe.datetime.now_datetime(); - frm.doc.time_logs.forEach(d => { - if (d.from_time && !d.to_time) { - d.to_time = completed_time; - frm.set_value('started_time' , ''); - frm.set_value('job_started', 0); - frm.save(); + prepare_timer_buttons: function(frm) { + frm.trigger("make_dashboard"); + if (!frm.doc.job_started) { + frm.add_custom_button(__("Start"), () => { + if (!frm.doc.employee) { + frappe.prompt({fieldtype: 'Link', label: __('Employee'), options: "Employee", + fieldname: 'employee'}, d => { + if (d.employee) { + frm.set_value("employee", d.employee); } - }) - }); - } + + frm.events.start_job(frm); + }, __("Enter Value"), __("Start")); + } else { + frm.events.start_job(frm); + } + }).addClass("btn-primary"); + } else if (frm.doc.status == "On Hold") { + frm.add_custom_button(__("Resume"), () => { + frappe.flags.resume_job = 1; + frm.events.start_job(frm); + }).addClass("btn-primary"); + } else { + frm.add_custom_button(__("Pause"), () => { + frappe.flags.pause_job = 1; + frm.set_value("status", "On Hold"); + frm.events.complete_job(frm); + }); + + frm.add_custom_button(__("Complete"), () => { + let completed_time = frappe.datetime.now_datetime(); + frm.trigger("hide_timer"); + + frappe.prompt({fieldtype: 'Float', label: __('Completed Quantity'), + fieldname: 'qty', reqd: 1, default: frm.doc.for_quantity}, data => { + frm.events.complete_job(frm, completed_time, data.qty); + }, __("Enter Value"), __("Complete")); + }).addClass("btn-primary"); } }, + start_job: function(frm) { + let row = frappe.model.add_child(frm.doc, 'Job Card Time Log', 'time_logs'); + row.from_time = frappe.datetime.now_datetime(); + frm.set_value('job_started', 1); + frm.set_value('started_time' , row.from_time); + frm.set_value("status", "Work In Progress"); + + if (!frappe.flags.resume_job) { + frm.set_value('current_time' , 0); + } + + frm.save(); + }, + + complete_job: function(frm, completed_time, completed_qty) { + frm.doc.time_logs.forEach(d => { + if (d.from_time && !d.to_time) { + d.to_time = completed_time || frappe.datetime.now_datetime(); + d.completed_qty = completed_qty || 0; + + if(frappe.flags.pause_job) { + let currentIncrement = moment(d.to_time).diff(moment(d.from_time),"seconds") || 0; + frm.set_value('current_time' , currentIncrement + (frm.doc.current_time || 0)); + } else { + frm.set_value('started_time' , ''); + frm.set_value('job_started', 0); + frm.set_value('current_time' , 0); + } + + frm.save(); + } + }); + }, + make_dashboard: function(frm) { if(frm.doc.__islocal) return; frm.dashboard.refresh(); const timer = ` -
+
00 : 00 @@ -63,11 +120,16 @@ frappe.ui.form.on('Job Card', { 00
`; - var section = frm.dashboard.add_section(timer); + var section = frm.toolbar.page.add_inner_message(timer); - if (frm.doc.started_time) { - let currentIncrement = moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.started_time),"seconds"); - initialiseTimer(); + let currentIncrement = frm.doc.current_time || 0; + if (frm.doc.started_time || frm.doc.current_time) { + if (frm.doc.status == "On Hold") { + updateStopwatch(currentIncrement); + } else { + currentIncrement += moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.started_time),"seconds"); + initialiseTimer(); + } function initialiseTimer() { const interval = setInterval(function() { @@ -93,6 +155,10 @@ frappe.ui.form.on('Job Card', { } }, + hide_timer: function(frm) { + frm.toolbar.page.inner_toolbar.find(".stopwatch").remove(); + }, + for_quantity: function(frm) { frm.doc.items = []; frm.call({ @@ -122,5 +188,22 @@ frappe.ui.form.on('Job Card', { timer: function(frm) { return `` + }, + + set_total_completed_qty: function(frm) { + frm.doc.total_completed_qty = 0; + frm.doc.time_logs.forEach(d => { + if (d.completed_qty) { + frm.doc.total_completed_qty += d.completed_qty; + } + }); + + refresh_field("total_completed_qty"); } -}); \ No newline at end of file +}); + +frappe.ui.form.on('Job Card Time Log', { + completed_qty: function(frm) { + frm.events.set_total_completed_qty(frm); + } +}) \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json index 39c5cce313b..fd39af1faad 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.json +++ b/erpnext/manufacturing/doctype/job_card/job_card.json @@ -1,1004 +1,300 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "PO-JOB.#####", - "beta": 0, - "creation": "2018-07-09 17:23:29.518745", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "autoname": "naming_series:", + "creation": "2018-07-09 17:23:29.518745", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "work_order", + "bom_no", + "workstation", + "operation", + "column_break_4", + "posting_date", + "company", + "remarks", + "production_section", + "production_item", + "item_name", + "for_quantity", + "wip_warehouse", + "column_break_12", + "employee", + "employee_name", + "status", + "project", + "timing_detail", + "time_logs", + "section_break_13", + "total_completed_qty", + "total_time_in_mins", + "column_break_15", + "section_break_8", + "items", + "more_information", + "operation_id", + "transferred_qty", + "requested_qty", + "column_break_20", + "barcode", + "job_started", + "started_time", + "current_time", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "work_order", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Work Order", - "length": 0, - "no_copy": 0, - "options": "Work Order", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "work_order", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Work Order", + "options": "Work Order", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 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 - }, + "fetch_from": "work_order.bom_no", + "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, - "fetch_if_empty": 0, - "fieldname": "workstation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Workstation", - "length": 0, - "no_copy": 0, - "options": "Workstation", - "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 - }, + "fieldname": "workstation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Workstation", + "options": "Workstation", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "operation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Operation", - "length": 0, - "no_copy": 0, - "options": "Operation", - "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 - }, + "fieldname": "operation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Operation", + "options": "Operation", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_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, - "default": "Today", - "fetch_if_empty": 0, - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Posting 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 - }, + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 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, - "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 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "for_quantity", - "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": "For Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "for_quantity", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Qty To Manufacture", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "wip_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "WIP Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "wip_warehouse", + "fieldtype": "Link", + "label": "WIP Warehouse", + "options": "Warehouse", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "timing_detail", - "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": "Timing Detail", - "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": "timing_detail", + "fieldtype": "Section Break", + "label": "Timing Detail" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "employee", - "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": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "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": "employee", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "time_logs", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Time Logs", - "length": 0, - "no_copy": 0, - "options": "Job Card Time Log", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "time_logs", + "fieldtype": "Table", + "label": "Time Logs", + "options": "Job Card Time Log" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_13", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_13", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "total_completed_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Completed Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_completed_qty", + "fieldtype": "Float", + "label": "Total Completed Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_15", - "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_15", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "total_time_in_mins", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Time in Mins", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_time_in_mins", + "fieldtype": "Float", + "label": "Total Time in Mins", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Raw Materials", - "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_8", + "fieldtype": "Section Break", + "label": "Raw Materials" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Items", - "length": 0, - "no_copy": 0, - "options": "Job Card Item", - "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": "items", + "fieldtype": "Table", + "label": "Items", + "options": "Job Card Item", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "more_information", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "more_information", + "fieldtype": "Section Break", + "label": "More Information" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "operation_id", - "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": "Operation ID", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "operation_id", + "fieldtype": "Data", + "hidden": 1, + "label": "Operation ID", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "transferred_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transferred Qty", - "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 - }, + "default": "0", + "fieldname": "transferred_qty", + "fieldtype": "Float", + "label": "Transferred Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "requested_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Requested Qty", - "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 - }, + "default": "0", + "fieldname": "requested_qty", + "fieldtype": "Float", + "label": "Requested Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "project", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Project", - "length": 0, - "no_copy": 0, - "options": "Project", - "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": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "remarks", - "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": "Remarks", - "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": "remarks", + "fieldtype": "Small Text", + "label": "Remarks" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_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 - }, + "fieldname": "column_break_20", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Open", - "fetch_if_empty": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 1, - "options": "Open\nWork In Progress\nMaterial Transferred\nSubmitted\nCancelled\nCompleted", - "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 - }, + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "no_copy": 1, + "options": "Open\nWork In Progress\nMaterial Transferred\nOn Hold\nSubmitted\nCancelled\nCompleted", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "job_started", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Job Started", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "job_started", + "fieldtype": "Check", + "hidden": 1, + "label": "Job Started", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "started_time", - "fieldtype": "Datetime", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Started Time", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "started_time", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Started Time", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Job Card", - "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", + "label": "Amended From", + "no_copy": 1, + "options": "Job Card", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "PO-JOB.#####", + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "PO-JOB.#####", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Read Only", + "label": "Employee Name" + }, + { + "fieldname": "production_section", + "fieldtype": "Section Break", + "label": "Production" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fetch_from": "work_order.production_item", + "fieldname": "production_item", + "fieldtype": "Read Only", + "label": "Production Item" + }, + { + "fieldname": "barcode", + "fieldtype": "Barcode", + "label": "Barcode", + "read_only": 1 + }, + { + "fetch_from": "work_order.item_name", + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, + { + "fieldname": "current_time", + "fieldtype": "Int", + "hidden": 1, + "label": "Current Time", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-03-10 17:38:37.499871", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Job Card", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "modified": "2020-03-24 13:08:57.926201", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Job Card", + "owner": "Administrator", "permissions": [ { "amend": 1, diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 9d2e620e584..eec7d242871 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt, time_diff_in_hours, get_datetime +from frappe.utils import flt, time_diff_in_hours, get_datetime, time_diff from frappe.model.mapper import get_mapped_doc from frappe.model.document import Document @@ -95,8 +95,9 @@ class JobCard(Document): if self.total_completed_qty <= 0.0: frappe.throw(_("Total completed qty must be greater than zero")) - if self.total_completed_qty > self.for_quantity: - frappe.throw(_("Total completed qty can not be greater than for quantity")) + if self.total_completed_qty != self.for_quantity: + frappe.throw(_("The total completed qty({0}) must be equal to qty to manufacture({1})" + .format(frappe.bold(self.total_completed_qty),frappe.bold(self.for_quantity)))) def update_work_order(self): if not self.work_order: @@ -172,6 +173,8 @@ class JobCard(Document): self.set_status(update_status) def set_status(self, update_status=False): + if self.status == "On Hold": return + self.status = { 0: "Open", 1: "Submitted", @@ -230,6 +233,7 @@ def make_stock_entry(source_name, target_doc=None): target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0) target.calculate_rate_and_amount() target.set_missing_values() + target.set_stock_entry_type() doclist = get_mapped_doc("Job Card", source_name, { "Job Card": { @@ -251,3 +255,48 @@ def make_stock_entry(source_name, target_doc=None): }, target_doc, set_missing_values) return doclist + +def time_diff_in_minutes(string_ed_date, string_st_date): + return time_diff(string_ed_date, string_st_date).total_seconds() / 60 + +@frappe.whitelist() +def get_job_details(start, end, filters=None): + events = [] + + event_color = { + "Completed": "#cdf5a6", + "Material Transferred": "#ffdd9e", + "Work In Progress": "#D3D3D3" + } + + from frappe.desk.reportview import get_filters_cond + conditions = get_filters_cond("Job Card", filters, []) + + job_cards = frappe.db.sql(""" SELECT `tabJob Card`.name, `tabJob Card`.work_order, + `tabJob Card`.employee_name, `tabJob Card`.status, ifnull(`tabJob Card`.remarks, ''), + min(`tabJob Card Time Log`.from_time) as from_time, + max(`tabJob Card Time Log`.to_time) as to_time + FROM `tabJob Card` , `tabJob Card Time Log` + WHERE + `tabJob Card`.name = `tabJob Card Time Log`.parent {0} + group by `tabJob Card`.name""".format(conditions), as_dict=1) + + for d in job_cards: + subject_data = [] + for field in ["name", "work_order", "remarks", "employee_name"]: + if not d.get(field): continue + + subject_data.append(d.get(field)) + + color = event_color.get(d.status) + job_card_data = { + 'from_time': d.from_time, + 'to_time': d.to_time, + 'name': d.name, + 'subject': '\n'.join(subject_data), + 'color': color if color else "#89bcde" + } + + events.append(job_card_data) + + return events diff --git a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js new file mode 100644 index 00000000000..cf07698ad6a --- /dev/null +++ b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js @@ -0,0 +1,21 @@ +frappe.views.calendar["Job Card"] = { + field_map: { + "start": "from_time", + "end": "to_time", + "id": "name", + "title": "subject", + "color": "color", + "allDay": "allDay", + "progress": "progress" + }, + gantt: true, + filters: [ + { + "fieldtype": "Link", + "fieldname": "employee", + "options": "Employee", + "label": __("Employee") + } + ], + get_events_method: "erpnext.manufacturing.doctype.job_card.job_card.get_job_details" +}; diff --git a/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json b/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json index 2aab71dee4f..9dd54dd6182 100644 --- a/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json +++ b/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json @@ -1,208 +1,57 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2019-03-08 23:56:43.187569", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "creation": "2019-03-08 23:56:43.187569", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "from_time", + "to_time", + "column_break_2", + "time_in_mins", + "completed_qty" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "from_time", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "From Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "from_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "From Time" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "to_time", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "To Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "to_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "To Time" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "time_in_mins", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Time In Mins", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "time_in_mins", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Time In Mins", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "completed_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Completed Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "completed_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Completed Qty", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-03-10 17:08:46.504910", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Job Card Time Log", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "modified": "2019-12-03 12:56:02.285448", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Job Card Time Log", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 3b24d0fa0ff..2b168d1d76d 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -71,12 +71,13 @@ frappe.ui.form.on('Production Plan', { }, __('Create')); } + frm.page.set_inner_btn_group_as_primary(__('Create')); frm.trigger("material_requirement"); const projected_qty_formula = ` + {%= format_currency(data[i]["invoiced"], data[0]["currency"] ) %} {% if(!filters.show_future_payments) { %} - + {%= format_currency(data[i]["paid"], data[0]["currency"]) %} + {% } %} + {%= format_currency(data[i]["outstanding"], data[0]["currency"]) %} {% if(filters.show_future_payments) { %} {% if(report.report_name === "Accounts Receivable") { %} @@ -234,8 +234,8 @@ {%= data[i]["po_no"] %} {% } %} - - + + {% } %} {% } %} {% } else { %} @@ -256,10 +256,10 @@ {% } else { %} {% } %} - - - - + + + + {% } %} {% } %} From cf2ab9b129afbf017d2ee222028c2bdacc1e8ab7 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 27 Mar 2020 20:43:17 +0530 Subject: [PATCH 248/929] fix: use setup from Supplier Quotation Controller --- .../supplier_quotation/supplier_quotation.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js index 39042b8b068..16061c61ba0 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js @@ -4,15 +4,17 @@ // attach required files {% include 'erpnext/public/js/controllers/buying.js' %}; -frappe.ui.form.on('Suppier Quotation', { - setup: function(frm) { - frm.custom_make_buttons = { - 'Purchase Order': 'Purchase Order' - } - } -}); - erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({ + setup: function() { + this.frm.custom_make_buttons = { + 'Purchase Order': 'Purchase Order', + 'Quotation': 'Quotation', + 'Subscription': 'Subscription' + } + + this._super(); + }, + refresh: function() { var me = this; this._super(); From 728edacfd4231e9878b8b9d17ef55a20b7d6b3a6 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sat, 28 Mar 2020 13:48:42 +0530 Subject: [PATCH 249/929] bug: Item-wise Sales History - Billed amount --- .../report/item_wise_sales_history/item_wise_sales_history.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index 1fc3663bed7..405004ece54 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -121,8 +121,8 @@ def get_columns(filters): }, { "label": _("Billed Amount"), - "fieldname": "rate", - "options": "billed_amount", + "fieldtype": "currency", + "fieldname": "billed_amount", "width": 120 }, { From 8f854c7d9397e5cd5038d0ab04dce4254577ba54 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 28 Mar 2020 14:56:45 +0530 Subject: [PATCH 250/929] fix: item code showing as mandatory even if the 'Item Naming By' is set as Naming Series in stock settings --- erpnext/public/js/utils/item_quick_entry.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/public/js/utils/item_quick_entry.js b/erpnext/public/js/utils/item_quick_entry.js index 2947d5b98eb..27ef107acef 100644 --- a/erpnext/public/js/utils/item_quick_entry.js +++ b/erpnext/public/js/utils/item_quick_entry.js @@ -8,12 +8,19 @@ frappe.ui.form.ItemQuickEntryForm = frappe.ui.form.QuickEntryForm.extend({ render_dialog: function() { this.mandatory = this.get_variant_fields().concat(this.mandatory); this.mandatory = this.mandatory.concat(this.get_attributes_fields()); + this.check_naming_series_based_on(); this._super(); this.init_post_render_dialog_operations(); this.preset_fields_for_template(); this.dialog.$wrapper.find('.edit-full').text(__('Edit in full page for more options like assets, serial nos, batches etc.')) }, + check_naming_series_based_on: function() { + if (frappe.defaults.get_default("item_naming_by") === "Naming Series") { + this.mandatory = this.mandatory.filter(d => d.fieldname !== "item_code"); + } + }, + init_post_render_dialog_operations: function() { this.dialog.fields_dict.attribute_html.$wrapper.append(frappe.render_template("item_quick_entry")); this.init_for_create_variant_trigger(); From f9ce7c2e18872cf2248ea1b0c156133cf712ff64 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sat, 28 Mar 2020 19:12:59 +0530 Subject: [PATCH 251/929] chore: [ux] filter warehouse based on company --- erpnext/stock/report/stock_ledger/stock_ledger.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.js b/erpnext/stock/report/stock_ledger/stock_ledger.js index 3fab3273b9e..1813c2b5bd7 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.js +++ b/erpnext/stock/report/stock_ledger/stock_ledger.js @@ -29,7 +29,13 @@ frappe.query_reports["Stock Ledger"] = { "fieldname":"warehouse", "label": __("Warehouse"), "fieldtype": "Link", - "options": "Warehouse" + "options": "Warehouse", + "get_query": function() { + const company = frappe.query_report.get_filter_value('company'); + return { + filters: { 'company': company } + } + } }, { "fieldname":"item_code", From 9578ead7f9d6ac25094108e341a9f7b9c09561d0 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sat, 28 Mar 2020 18:44:53 +0530 Subject: [PATCH 252/929] fix: no server side validations for accounts in asset category --- .../doctype/asset_category/asset_category.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py index fc08841be99..855c7094115 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -11,12 +11,29 @@ from frappe.model.document import Document class AssetCategory(Document): def validate(self): self.validate_finance_books() + self.validate_accounts() def validate_finance_books(self): for d in self.finance_books: for field in ("Total Number of Depreciations", "Frequency of Depreciation"): if cint(d.get(frappe.scrub(field)))<1: frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError) + + def validate_accounts(self): + account_type_map = { + 'fixed_asset_account': { 'account_type': 'Fixed Asset' }, + 'accumulated_depreciation_account': { 'account_type': 'Accumulated Depreciation' }, + 'depreciation_expense_account': { 'account_type': 'Expense' }, + 'capital_work_in_progress_account': { 'account_type': 'Capital Work in Progress' } + } + for d in self.accounts: + for account in account_type_map.keys(): + if d.get(account): + account_type = frappe.db.get_value('Account', d.get(account), 'account_type') + if account_type != account_type_map[account]['account_type']: + frappe.throw(_("Row {}: {} should be a {} account".format(d.idx, frappe.bold(frappe.unscrub(account)), + frappe.bold(account_type_map[account]['account_type']))), title=_("Invalid Account")) + @frappe.whitelist() def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None): From 58351de79741c20bac9ba7d6e49c49f428ee72c4 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sat, 28 Mar 2020 16:11:23 +0530 Subject: [PATCH 253/929] fix: payment request status fixes --- .../payment_request/payment_request.json | 1837 +++-------------- .../payment_request/payment_request.py | 8 +- .../payment_request/payment_request_list.js | 8 +- 3 files changed, 346 insertions(+), 1507 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index bff995ec5a5..97ae5ffde36 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -1,1560 +1,387 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2015-12-15 22:23:24.745065", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 0, - "engine": "InnoDB", + "autoname": "naming_series:", + "creation": "2015-12-15 22:23:24.745065", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "payment_request_type", + "transaction_date", + "column_break_2", + "naming_series", + "mode_of_payment", + "party_details", + "party_type", + "party", + "column_break_4", + "reference_doctype", + "reference_name", + "transaction_details", + "grand_total", + "is_a_subscription", + "column_break_18", + "currency", + "subscription_section", + "subscription_plans", + "bank_account_details", + "bank_account", + "bank", + "bank_account_no", + "account", + "column_break_11", + "iban", + "branch_code", + "swift_number", + "recipient_and_message", + "print_format", + "email_to", + "subject", + "column_break_9", + "payment_gateway_account", + "status", + "make_sales_invoice", + "section_break_10", + "message", + "message_examples", + "mute_email", + "payment_url", + "section_break_7", + "payment_gateway", + "payment_account", + "payment_order", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Inward", - "fieldname": "payment_request_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": "Payment Request Type", - "length": 0, - "no_copy": 0, - "options": "Outward\nInward", - "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 - }, + "default": "Inward", + "fieldname": "payment_request_type", + "fieldtype": "Select", + "label": "Payment Request Type", + "options": "Outward\nInward", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transaction_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": "Transaction 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 - }, + "fieldname": "transaction_date", + "fieldtype": "Date", + "label": "Transaction Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "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": 1, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 1, - "options": "ACC-PRQ-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Series", + "no_copy": 1, + "options": "ACC-PRQ-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mode_of_payment", - "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": "Mode of Payment", - "length": 0, - "no_copy": 0, - "options": "Mode of Payment", - "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": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party_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": "Party Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "party_details", + "fieldtype": "Section Break", + "label": "Party Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party_type", - "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": "Party Type", - "length": 0, - "no_copy": 0, - "options": "DocType", - "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": "party_type", + "fieldtype": "Link", + "label": "Party Type", + "options": "DocType" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party", - "fieldtype": "Dynamic 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": "Party", - "length": 0, - "no_copy": 0, - "options": "party_type", - "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": "party", + "fieldtype": "Dynamic Link", + "label": "Party", + "options": "party_type" + }, { - "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": "reference_doctype", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Reference Doctype", - "length": 0, - "no_copy": 1, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Reference Doctype", + "no_copy": 1, + "options": "DocType", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "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": 0, - "in_standard_filter": 1, - "label": "Reference Name", - "length": 0, - "no_copy": 1, - "options": "reference_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Reference Name", + "no_copy": 1, + "options": "reference_doctype", + "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": "transaction_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": "Transaction Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "transaction_details", + "fieldtype": "Section Break", + "label": "Transaction Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Amount in customer's currency", - "fieldname": "grand_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Amount in customer's currency", + "fieldname": "grand_total", + "fieldtype": "Currency", + "label": "Amount", + "options": "currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_a_subscription", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is a Subscription", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "is_a_subscription", + "fieldtype": "Check", + "label": "Is a Subscription" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_18", - "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_18", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "currency", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transaction Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "currency", + "fieldtype": "Link", + "label": "Transaction Currency", + "options": "Currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "eval:doc.is_a_subscription", - "fieldname": "subscription_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subscription Section", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.is_a_subscription", + "fieldname": "subscription_section", + "fieldtype": "Section Break", + "label": "Subscription Section" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subscription_plans", - "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": "Subscription Plans", - "length": 0, - "no_copy": 0, - "options": "Subscription Plan Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "subscription_plans", + "fieldtype": "Table", + "label": "Subscription Plans", + "options": "Subscription Plan Detail" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "bank_account_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": "Bank Account Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "bank_account_details", + "fieldtype": "Section Break", + "label": "Bank Account Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Account", - "length": 0, - "no_copy": 0, - "options": "Bank Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bank_account", + "fieldtype": "Link", + "label": "Bank Account", + "options": "Bank Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "bank_account.bank", - "fieldname": "bank", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank", - "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 - }, + "fetch_from": "bank_account.bank", + "fieldname": "bank", + "fieldtype": "Read Only", + "label": "Bank" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "bank_account.bank_account_no", - "fieldname": "bank_account_no", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Account No", - "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 - }, + "fetch_from": "bank_account.bank_account_no", + "fieldname": "bank_account_no", + "fieldtype": "Read Only", + "label": "Bank Account No" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "bank_account.account", - "fieldname": "account", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Account", - "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 - }, + "fetch_from": "bank_account.account", + "fieldname": "account", + "fieldtype": "Read Only", + "label": "Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "bank_account.iban", - "fieldname": "iban", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "IBAN", - "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 - }, + "fetch_from": "bank_account.iban", + "fieldname": "iban", + "fieldtype": "Read Only", + "label": "IBAN" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "bank_account.branch_code", - "fieldname": "branch_code", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Branch Code", - "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 - }, + "fetch_from": "bank_account.branch_code", + "fieldname": "branch_code", + "fieldtype": "Read Only", + "label": "Branch Code" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "bank_account.swift_number", - "fieldname": "swift_number", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "SWIFT Number", - "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 - }, + "fetch_from": "bank_account.swift_number", + "fieldname": "swift_number", + "fieldtype": "Read Only", + "label": "SWIFT Number" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.payment_request_type == 'Inward'", - "fieldname": "recipient_and_message", - "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": "Recipient Message And Payment Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval: doc.payment_request_type == 'Inward'", + "fieldname": "recipient_and_message", + "fieldtype": "Section Break", + "label": "Recipient Message And Payment Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "print_format", - "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": "Print Format", - "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 - }, + "fieldname": "print_format", + "fieldtype": "Select", + "label": "Print Format" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "email_to", - "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": "To", - "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": "email_to", + "fieldtype": "Data", + "in_global_search": 1, + "label": "To" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subject", - "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": "Subject", - "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": "subject", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Subject" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "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_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.payment_request_type == 'Inward'", - "fieldname": "payment_gateway_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Gateway Account", - "length": 0, - "no_copy": 0, - "options": "Payment Gateway Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval: doc.payment_request_type == 'Inward'", + "fieldname": "payment_gateway_account", + "fieldtype": "Link", + "label": "Payment Gateway Account", + "options": "Payment Gateway Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Draft", - "fieldname": "status", - "fieldtype": "Select", - "hidden": 1, - "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": 0, - "options": "\nDraft\nInitiated\nPayment Ordered\nPaid\nFailed\nCancelled", - "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 - }, + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "\nDraft\nRequested\nInitiated\nPartially Paid\nPayment Ordered\nPaid\nFailed\nCancelled", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.reference_doctype==\"Sales Order\"", - "fieldname": "make_sales_invoice", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Make Sales 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 - }, + "default": "0", + "depends_on": "eval:doc.reference_doctype==\"Sales Order\"", + "fieldname": "make_sales_invoice", + "fieldtype": "Check", + "hidden": 1, + "label": "Make Sales Invoice", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.payment_request_type == 'Inward'", - "fieldname": "section_break_10", - "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 - }, + "depends_on": "eval: doc.payment_request_type == 'Inward'", + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "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 - }, + "fieldname": "message", + "fieldtype": "Text", + "label": "Message" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "message_examples", - "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": "Message Examples", - "length": 0, - "no_copy": 0, - "options": "
Message Example
\n\n<p>Dear {{ doc.contact_person }},</p>\n\n<p>Requesting payment for {{ doc.doctype }}, {{ doc.name }} for {{ doc.grand_total }}.</p>\n\n<a href=\"{{ payment_url }}\"> click here to pay </a>\n\n
\n", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "message_examples", + "fieldtype": "HTML", + "label": "Message Examples", + "options": "
Message Example
\n\n<p>Dear {{ doc.contact_person }},</p>\n\n<p>Requesting payment for {{ doc.doctype }}, {{ doc.name }} for {{ doc.grand_total }}.</p>\n\n<a href=\"{{ payment_url }}\"> click here to pay </a>\n\n
\n", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mute_email", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Mute Email", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "mute_email", + "fieldtype": "Check", + "hidden": 1, + "label": "Mute Email", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_url", - "fieldtype": "Small Text", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "payment_url", - "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": "payment_url", + "fieldtype": "Small Text", + "hidden": 1, + "label": "payment_url", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "doc.payment_gateway_account", - "columns": 0, - "depends_on": "eval: doc.payment_request_type == 'Inward'", - "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, - "label": "Payment Gateway Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "doc.payment_gateway_account", + "depends_on": "eval: doc.payment_request_type == 'Inward'", + "fieldname": "section_break_7", + "fieldtype": "Section Break", + "label": "Payment Gateway Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "payment_gateway_account.payment_gateway", - "fieldname": "payment_gateway", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Gateway", - "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 - }, + "fetch_from": "payment_gateway_account.payment_gateway", + "fieldname": "payment_gateway", + "fieldtype": "Read Only", + "label": "Payment Gateway" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "payment_gateway_account.payment_account", - "fieldname": "payment_account", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Account", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "fetch_from": "payment_gateway_account.payment_account", + "fieldname": "payment_account", + "fieldtype": "Read Only", + "label": "Payment Account", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_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": "Payment Order", - "length": 0, - "no_copy": 0, - "options": "Payment Order", - "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": "payment_order", + "fieldtype": "Link", + "label": "Payment Order", + "options": "Payment Order", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Payment Request", - "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", + "label": "Amended From", + "no_copy": 1, + "options": "Payment Request", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-02-18 18:52:34.203239", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Payment Request", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "modified": "2020-03-28 16:07:31.960798", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Request", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 6b74f477f2b..b18a938fbb0 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -66,6 +66,8 @@ class PaymentRequest(Document): if self.payment_request_type == 'Outward': self.db_set('status', 'Initiated') return + elif self.payment_request_type == 'Inward': + self.db_set('status', 'Requested') send_mail = self.payment_gateway_validation() ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) @@ -422,10 +424,14 @@ def make_status_as_paid(doc, method): "docstatus": 1}) if payment_request_name: + outstanding_amt = frappe.get_value(ref.reference_doctype, ref.reference_name, 'outstanding_amount') doc = frappe.get_doc("Payment Request", payment_request_name) - if doc.status != "Paid": + if doc.status != "Paid" and outstanding_amt <= 0: doc.db_set('status', 'Paid') frappe.db.commit() + elif doc.status != "Partially Paid" and outstanding_amt != doc.grand_total: + doc.db_set('status', 'Partially Paid') + frappe.db.commit() def get_dummy_message(doc): return frappe.render_template("""{% if doc.contact_person -%} diff --git a/erpnext/accounts/doctype/payment_request/payment_request_list.js b/erpnext/accounts/doctype/payment_request/payment_request_list.js index 0caf1c2f7ff..72833d235f8 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request_list.js +++ b/erpnext/accounts/doctype/payment_request/payment_request_list.js @@ -4,14 +4,20 @@ frappe.listview_settings['Payment Request'] = { if(doc.status == "Draft") { return [__("Draft"), "darkgrey", "status,=,Draft"]; } + if(doc.status == "Requested") { + return [__("Requested"), "green", "status,=,Requested"]; + } else if(doc.status == "Initiated") { return [__("Initiated"), "green", "status,=,Initiated"]; } + else if(doc.status == "Partially Paid") { + return [__("Partially Paid"), "orange", "status,=,Partially Paid"]; + } else if(doc.status == "Paid") { return [__("Paid"), "blue", "status,=,Paid"]; } else if(doc.status == "Cancelled") { - return [__("Cancelled"), "orange", "status,=,Cancelled"]; + return [__("Cancelled"), "red", "status,=,Cancelled"]; } } } From 1125ed883039cc2eb3fc09333c0bde28b199505e Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sat, 28 Mar 2020 16:39:22 +0530 Subject: [PATCH 254/929] fix: currency formatting in gl entry dr cr field --- erpnext/accounts/doctype/gl_entry/gl_entry.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json index e64bc9e0c78..2214811d8b3 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.json +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json @@ -114,13 +114,13 @@ "fieldname": "debit_in_account_currency", "fieldtype": "Currency", "label": "Debit Amount in Account Currency", - "options": "currency" + "options": "account_currency" }, { "fieldname": "credit_in_account_currency", "fieldtype": "Currency", "label": "Credit Amount in Account Currency", - "options": "currency" + "options": "account_currency" }, { "fieldname": "against", @@ -250,7 +250,7 @@ "icon": "fa fa-list", "idx": 1, "in_create": 1, - "modified": "2020-02-10 04:54:57.777905", + "modified": "2020-03-28 16:22:33.766994", "modified_by": "Administrator", "module": "Accounts", "name": "GL Entry", From 8ba661b8edb54454e4351cfc416bdac56976aea3 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sat, 28 Mar 2020 20:11:26 +0530 Subject: [PATCH 255/929] chore: hyperlinks in cannot cancel message --- erpnext/controllers/buying_controller.py | 2 +- erpnext/manufacturing/doctype/work_order/work_order.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index a47507eaf7b..61bd5032cee 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -731,7 +731,7 @@ class BuyingController(StockController): asset.supplier = None if asset.docstatus == 1 and delete_asset: frappe.throw(_('Cannot cancel this document as it is linked with submitted asset {0}.\ - Please cancel the it to continue.').format(asset.name)) + Please cancel the it to continue.').format(frappe.utils.get_link_to_form('Asset', asset.name))) asset.flags.ignore_validate_update_after_submit = True asset.flags.ignore_mandatory = True diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 83579414812..bd2ae6c36b1 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -274,7 +274,7 @@ class WorkOrder(Document): stock_entry = frappe.db.sql("""select name from `tabStock Entry` where work_order = %s and docstatus = 1""", self.name) if stock_entry: - frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(stock_entry[0][0])) + frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(frappe.utils.get_link_to_form('Stock Entry', stock_entry[0][0]))) def update_planned_qty(self): update_bin_qty(self.production_item, self.fg_warehouse, { From dc04cbf9a98282bc8ff6bd87cb358be126911832 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sun, 29 Mar 2020 12:52:16 +0530 Subject: [PATCH 256/929] fix: cannot view report for course doctype - permission problem --- erpnext/education/doctype/course/course.json | 26 +++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/erpnext/education/doctype/course/course.json b/erpnext/education/doctype/course/course.json index 68426c36be1..da10db1857a 100644 --- a/erpnext/education/doctype/course/course.json +++ b/erpnext/education/doctype/course/course.json @@ -74,7 +74,7 @@ } ], "image_field": "hero_image", - "modified": "2019-06-12 12:34:23.748157", + "modified": "2020-03-29 12:50:27.677589", "modified_by": "Administrator", "module": "Education", "name": "Course", @@ -103,6 +103,30 @@ "role": "Instructor", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Education Manager", + "share": 1, + "write": 1 } ], "restrict_to_domain": "Education", From 1dd0c8f54c005a223296076c28ab40fd20e2acce Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sat, 28 Mar 2020 14:07:09 +0530 Subject: [PATCH 257/929] fix: customer group price list not fetched in pos --- .../accounts/doctype/sales_invoice/sales_invoice.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index fcddab40b8f..919af057ac1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -429,13 +429,16 @@ class SalesInvoice(SellingController): if (not for_validate) or (for_validate and not self.get(fieldname)): self.set(fieldname, pos.get(fieldname)) - customer_price_list = frappe.get_value("Customer", self.customer, 'default_price_list') - if pos.get("company_address"): self.company_address = pos.get("company_address") - if not customer_price_list: - self.set('selling_price_list', pos.get('selling_price_list')) + customer_price_list, customer_group = frappe.get_value("Customer", self.customer, ['default_price_list', 'customer_group']) + + customer_group_price_list = frappe.get_value("Customer Group", customer_group, 'default_price_list') + + selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list') + + self.set('selling_price_list', selling_price_list) if not for_validate: self.update_stock = cint(pos.get("update_stock")) From 7e7787c298508dbb741403b9dc5a26c8d743ea2d Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 29 Mar 2020 16:33:30 +0530 Subject: [PATCH 258/929] fix: not able to add equity account in the chart of accounts --- erpnext/accounts/doctype/account/account.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 1407d5f5fe3..0a72d4fa4e6 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -102,15 +102,15 @@ class Account(NestedSet): if not frappe.db.get_value("Account", {'account_name': self.account_name, 'company': ancestors[0]}, 'name'): frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0])) - else: + elif self.parent_account: descendants = get_descendants_of('Company', self.company) if not descendants: return parent_acc_name_map = {} parent_acc_name, parent_acc_number = frappe.db.get_value('Account', self.parent_account, \ ["account_name", "account_number"]) - filters = { + filters = { "company": ["in", descendants], - "account_name": parent_acc_name, + "account_name": parent_acc_name, } if parent_acc_number: filters["account_number"] = parent_acc_number From 83111a4a2a05c2b9a493bf8f834534703a426c81 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 31 Mar 2020 10:45:37 +0530 Subject: [PATCH 259/929] fix: Expense account currency validation in Landed Cost voucher (#21074) * fix: Expense account currency validation in Landed Cost voucher * fix: Remove unused imports --- erpnext/controllers/queries.py | 14 +++++++++----- .../landed_cost_voucher/landed_cost_voucher.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index d18f8e54d8f..163ef72ee10 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +import erpnext from frappe.desk.reportview import get_match_cond, get_filters_cond from frappe.utils import nowdate, getdate from collections import defaultdict @@ -129,23 +130,26 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): }) def tax_account_query(doctype, txt, searchfield, start, page_len, filters): + company_currency = erpnext.get_company_currency(filters.get('company')) + tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount where tabAccount.docstatus!=2 and account_type in (%s) and is_group = 0 and company = %s + and account_currency = %s and `%s` LIKE %s order by idx desc, name limit %s, %s""" % - (", ".join(['%s']*len(filters.get("account_type"))), "%s", searchfield, "%s", "%s", "%s"), - tuple(filters.get("account_type") + [filters.get("company"), "%%%s%%" % txt, + (", ".join(['%s']*len(filters.get("account_type"))), "%s", "%s", searchfield, "%s", "%s", "%s"), + tuple(filters.get("account_type") + [filters.get("company"), company_currency, "%%%s%%" % txt, start, page_len])) if not tax_accounts: tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount where tabAccount.docstatus!=2 and is_group = 0 - and company = %s and `%s` LIKE %s limit %s, %s""" - % ("%s", searchfield, "%s", "%s", "%s"), - (filters.get("company"), "%%%s%%" % txt, start, page_len)) + and company = %s and account_currency = %s and `%s` LIKE %s limit %s, %s""" #nosec + % ("%s", "%s", searchfield, "%s", "%s", "%s"), + (filters.get("company"), company_currency, "%%%s%%" % txt, start, page_len)) return tax_accounts diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index d97b699a0f3..5ad0e13db9a 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -8,6 +8,7 @@ from frappe.utils import flt from frappe.model.meta import get_field_precision from frappe.model.document import Document from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos +from erpnext.accounts.doctype.account.account import get_account_currency class LandedCostVoucher(Document): def get_items_from_purchase_receipts(self): @@ -43,6 +44,7 @@ class LandedCostVoucher(Document): else: self.validate_applicable_charges_for_item() self.validate_purchase_receipts() + self.validate_expense_accounts() self.set_total_taxes_and_charges() def check_mandatory(self): @@ -71,6 +73,14 @@ class LandedCostVoucher(Document): frappe.throw(_("Row {0}: Cost center is required for an item {1}") .format(item.idx, item.item_code)) + def validate_expense_accounts(self): + company_currency = erpnext.get_company_currency(self.company) + for account in self.taxes: + if get_account_currency(account.expense_account) != company_currency: + frappe.throw(msg=_(""" Row {0}: Expense account currency should be same as company's default currency. + Please select expense account with account currency as {1}""") + .format(account.idx, frappe.bold(company_currency)), title=_("Invalid Account Currency")) + def set_total_taxes_and_charges(self): self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")]) From f492ba1e2dff0843d2d0383bfd55c4b1bd056790 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 31 Mar 2020 10:48:57 +0530 Subject: [PATCH 260/929] fix: auto created asset message (#21109) * fix: auto created asset message * Update erpnext/controllers/buying_controller.py Co-authored-by: Nabin Hait --- erpnext/controllers/buying_controller.py | 33 +++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 61bd5032cee..ff07b59bd9b 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -658,19 +658,32 @@ class BuyingController(StockController): # If asset has to be auto created # Check for asset naming series if item_data.get('asset_naming_series'): + created_assets = [] + for qty in range(cint(d.qty)): - self.make_asset(d) - is_plural = 's' if cint(d.qty) != 1 else '' - messages.append(_('{0} Asset{2} Created for {1}').format(cint(d.qty), d.item_code, is_plural)) + asset = self.make_asset(d) + created_assets.append(asset) + + if len(created_assets) > 5: + # dont show asset form links if more than 5 assets are created + messages.append(_('{} Asset{} created for {}').format(len(created_assets), is_plural, frappe.bold(d.item_code))) + else: + assets_link = list(map(lambda d: frappe.utils.get_link_to_form('Asset', d), created_assets)) + assets_link = frappe.bold(','.join(assets_link)) + + is_plural = 's' if len(created_assets) != 1 else '' + messages.append( + _('Asset{} {assets_link} created for {}').format(is_plural, frappe.bold(d.item_code), assets_link=assets_link) + ) else: - frappe.throw(_("Row {1}: Asset Naming Series is mandatory for the auto creation for item {0}") - .format(d.item_code, d.idx)) + frappe.throw(_("Row {}: Asset Naming Series is mandatory for the auto creation for item {}") + .format(d.idx, frappe.bold(d.item_code))) else: - messages.append(_("Assets not created for {0}. You will have to create asset manually.") - .format(d.item_code)) + messages.append(_("Assets not created for {0}. You will have to create asset manually.") + .format(frappe.bold(d.item_code))) for message in messages: - frappe.msgprint(message, title="Success") + frappe.msgprint(message, title="Success", indicator="green") def make_asset(self, row): if not row.asset_location: @@ -702,6 +715,8 @@ class BuyingController(StockController): asset.set_missing_values() asset.insert() + return asset.name + def update_fixed_asset(self, field, delete_asset = False): for d in self.get("items"): if d.is_fixed_asset: @@ -1012,4 +1027,4 @@ def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty available_batches.append({'batch': batch, 'qty': available_qty}) required_qty -= available_qty - return available_batches \ No newline at end of file + return available_batches From d13e7d00b0b07e8c4be404dd2fbfe96ca769645c Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Tue, 31 Mar 2020 10:50:53 +0530 Subject: [PATCH 261/929] fix: email_to, party_type and party are not set in payment request (#21085) * fix: email_to, party_type and party are not set in payment request when order made from portal * fix: email_to, party_type and party are not set in payment request when order made from portal Co-authored-by: Anupam K --- erpnext/accounts/doctype/payment_request/payment_request.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 6b74f477f2b..34132e9a915 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -317,13 +317,13 @@ def make_payment_request(**args): "payment_request_type": args.get("payment_request_type"), "currency": ref_doc.currency, "grand_total": grand_total, - "email_to": args.recipient_id or "", + "email_to": args.recipient_id or ref_doc.owner, "subject": _("Payment Request for {0}").format(args.dn), "message": gateway_account.get("message") or get_dummy_message(ref_doc), "reference_doctype": args.dt, "reference_name": args.dn, - "party_type": args.get("party_type"), - "party": args.get("party"), + "party_type": args.get("party_type") or "Customer", + "party": args.get("party") or ref_doc.customer, "bank_account": bank_account }) From b80213d65dbc34e137ca48225d24094ce122a607 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 31 Mar 2020 10:52:51 +0530 Subject: [PATCH 262/929] fix: Healthcare Domain Issues (#21112) * fix (Lab Test): None TypeError in Patient Medical Record * fix: Lab Test Template data import failing in Lab Test Item creation * fix: disabled Procedure Template shown as enabled in List View * fix: change item_code from Link to Data to avoid item not found error * fix: disabled patient shown as enabled * fix: disabled practitioner schedule shown as enabled in list view * fix: appointment reminders not working * fix: Batch not getting fetched in Clinical Procedure Item --- .../clinical_procedure/clinical_procedure.js | 20 +- .../clinical_procedure_template.js | 33 +- .../clinical_procedure_template.json | 969 ++++------------- .../clinical_procedure_template.py | 100 +- .../healthcare_settings.json | 1 + .../healthcare/doctype/lab_test/lab_test.py | 14 +- .../lab_test_template/lab_test_template.py | 7 +- erpnext/healthcare/doctype/patient/patient.js | 2 +- .../healthcare/doctype/patient/patient.json | 26 +- erpnext/healthcare/doctype/patient/patient.py | 6 +- .../patient_appointment.js | 15 +- .../patient_appointment.json | 993 ++---------------- .../patient_appointment.py | 36 +- .../patient_encounter/patient_encounter.js | 2 +- .../practitioner_schedule.json | 209 ++-- erpnext/hooks.py | 4 +- 16 files changed, 451 insertions(+), 1986 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index fa9188449e3..cf8c9b9ca35 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -5,20 +5,16 @@ frappe.ui.form.on('Clinical Procedure', { setup: function(frm) { frm.set_query('batch_no', 'items', function(doc, cdt, cdn) { var item = locals[cdt][cdn]; - if(!item.item_code) { - frappe.throw(__("Please enter Item Code to get Batch Number")); + if (!item.item_code) { + frappe.throw(__('Please enter Item Code to get Batch Number')); } else { + let filters = {'item_code': item.item_code}; + if (frm.doc.status == 'In Progress') { - var filters = { - 'item_code': item.item_code, - 'posting_date': frm.doc.start_date || frappe.datetime.nowdate() - }; - if(frm.doc.warehouse) filters["warehouse"] = frm.doc.warehouse; - } else { - filters = { - 'item_code': item.item_code - }; + filters['posting_date'] = frm.doc.start_date || frappe.datetime.nowdate(); + if (frm.doc.warehouse) filters['warehouse'] = frm.doc.warehouse; } + return { query : "erpnext.controllers.queries.get_batch_no", filters: filters @@ -29,7 +25,7 @@ frappe.ui.form.on('Clinical Procedure', { refresh: function(frm) { frm.set_query("patient", function () { return { - filters: {"disabled": 0} + filters: {"status": "Active"} }; }); frm.set_query("appointment", function () { diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js index 583728dd185..de0f9c43003 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js @@ -31,17 +31,7 @@ frappe.ui.form.on('Clinical Procedure Template', { if(!frm.doc.__islocal) { cur_frm.add_custom_button(__('Change Item Code'), function() { change_template_code(frm.doc); - } ); - if(frm.doc.disabled == 1){ - cur_frm.add_custom_button(__('Enable Template'), function() { - enable_template(frm.doc); - } ); - } - else{ - cur_frm.add_custom_button(__('Disable Template'), function() { - disable_template(frm.doc); - } ); - } + }); } } }); @@ -52,27 +42,6 @@ var mark_change_in_item = function(frm) { } }; -var disable_template = function(doc){ - frappe.call({ - method: "erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.disable_enable_template", - args: {status: 1, name: doc.name, item_code: doc.item_code, is_billable: doc.is_billable}, - callback: function(){ - cur_frm.reload_doc(); - } - }); -}; - -var enable_template = function(doc){ - frappe.call({ - method: "erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.disable_enable_template", - args: {status: 0, name: doc.name, item_code: doc.item_code, is_billable: doc.is_billable}, - callback: function(){ - cur_frm.reload_doc(); - } - }); -}; - - var change_template_code = function(doc){ var d = new frappe.ui.Dialog({ title:__("Change Template Code"), diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json index 26564a34ce4..7ae4be84a28 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json @@ -1,807 +1,230 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:template", - "beta": 1, - "creation": "2017-10-05 14:59:55.438359", - "custom": 0, - "description": "Procedure Template", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:template", + "beta": 1, + "creation": "2017-10-05 14:59:55.438359", + "description": "Procedure Template", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "template", + "item", + "item_code", + "item_group", + "description", + "column_break_5", + "disabled", + "is_billable", + "rate", + "medical_department", + "consumables", + "consume_stock", + "items", + "sample_collection", + "sample", + "sample_uom", + "sample_qty", + "column_break_21", + "sample_details", + "change_in_item" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "template", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Template Name", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "template", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Template Name", + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_code", + "fieldname": "item_code", + "fieldtype": "Data", + "label": "Item Code", + "options": "Item", + "reqd": 1 + }, + { + "fieldname": "item_group", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 1, - "options": "Item", - "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 - }, + "ignore_user_permissions": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Item Group", + "options": "Item Group", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Item Group", - "length": 0, - "no_copy": 0, - "options": "Item Group", - "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 - }, + "fieldname": "medical_department", + "fieldtype": "Link", + "label": "Medical Department", + "options": "Medical Department" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "medical_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": "Medical Department", - "length": 0, - "no_copy": 0, - "options": "Medical 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 - }, + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, { - "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 - }, + "default": "0", + "fieldname": "is_billable", + "fieldtype": "Check", + "label": "Is Billable" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_billable", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Billable", - "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 - }, + "depends_on": "is_billable", + "fieldname": "rate", + "fieldtype": "Float", + "label": "Rate", + "mandatory_depends_on": "is_billable" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_billable", - "fieldname": "rate", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rate", - "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": "description", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "label": "Description", + "no_copy": 1, + "reqd": 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": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 1, - "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 - }, + "default": "0", + "fieldname": "consume_stock", + "fieldtype": "Check", + "label": "Allow Stock Consumption", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "section_break_9", - "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": "consumables", + "fieldtype": "Section Break", + "label": "Consumables" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "consume_stock", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Stock Consumption", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.consume_stock == 1", + "fieldname": "items", + "fieldtype": "Table", + "ignore_user_permissions": 1, + "label": "Items", + "options": "Clinical Procedure Item" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.consume_stock == 1", - "fieldname": "consumables", - "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": "Consumables", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "sample_collection", + "fieldtype": "Section Break", + "label": "Sample Collection" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Items", - "length": 0, - "no_copy": 0, - "options": "Clinical Procedure Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sample", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Sample", + "options": "Lab Test Sample" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "depends_on": "", - "fieldname": "sample_collection", - "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": "Sample Collection", - "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 - }, + "fetch_from": "sample.sample_uom", + "fieldname": "sample_uom", + "fieldtype": "Data", + "label": "UOM" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample", - "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": "Sample", - "length": 0, - "no_copy": 0, - "options": "Lab Test Sample", - "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": "sample_qty", + "fieldtype": "Float", + "label": "Quantity" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "sample.sample_uom", - "fieldname": "sample_uom", - "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": "UOM", - "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 - }, + "fieldname": "column_break_21", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sample_details", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "label": "Collection Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_21", - "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 - }, + "default": "0", + "fieldname": "change_in_item", + "fieldtype": "Check", + "hidden": 1, + "label": "Change In Item", + "no_copy": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample_details", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Collection Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "change_in_item", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Change In Item", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "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": "disabled", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disabled", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "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": "item", - "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": "Item", - "length": 0, - "no_copy": 1, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "item", + "fieldtype": "Link", + "label": "Item", + "no_copy": 1, + "options": "Item", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-02-12 11:37:18.713344", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Clinical Procedure Template", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-02-27 16:32:55.780893", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Clinical Procedure Template", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "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": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 - }, + }, { - "amend": 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": "Nursing User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 - }, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Nursing User", + "share": 1 + }, { - "amend": 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": "Physician", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Physician", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "template", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "template", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 + ], + "restrict_to_domain": "Healthcare", + "search_fields": "template", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "template", + "track_changes": 1, + "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py index 141329b3db1..00fad910a4f 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py @@ -9,26 +9,36 @@ from frappe.model.document import Document from frappe.utils import nowdate class ClinicalProcedureTemplate(Document): - def on_update(self): - #Item and Price List update --> if (change_in_item) - if(self.change_in_item and self.is_billable == 1 and self.item): - updating_item(self) - if(self.rate != 0.0): - updating_rate(self) - elif(self.is_billable == 0 and self.item): - frappe.db.set_value("Item",self.item,"disabled",1) - - frappe.db.set_value(self.doctype,self.name,"change_in_item",0) - self.reload() + def validate(self): + self.enable_disable_item() def after_insert(self): create_item_from_template(self) + def on_update(self): + #Item and Price List update --> if (change_in_item) + if self.change_in_item and self.is_billable == 1 and self.item: + updating_item(self) + if self.rate != 0.0: + updating_rate(self) + elif self.is_billable == 0 and self.item: + frappe.db.set_value('Item',self.item,'disabled',1) + + frappe.db.set_value(self.doctype,self.name,'change_in_item',0) + self.reload() + + def enable_disable_item(self): + if self.is_billable: + if self.disabled: + frappe.db.set_value('Item', self.item, 'disabled', 1) + else: + frappe.db.set_value('Item', self.item, 'disabled', 0) + #Call before delete the template def on_trash(self): if(self.item): try: - frappe.delete_doc("Item",self.item) + frappe.delete_doc('Item',self.item) except Exception: frappe.throw(_("""Not permitted. Please disable the Procedure Template""")) @@ -40,7 +50,7 @@ class ClinicalProcedureTemplate(Document): and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)""", (args.get('item_code'), nowdate()), as_dict = 1) if not item: - frappe.throw(_("Item {0} is not active or end of life has been reached").format(args.get('item_code'))) + frappe.throw(_('Item {0} is not active or end of life has been reached').format(args.get('item_code'))) item = item[0] @@ -63,46 +73,47 @@ def updating_rate(self): item_code=%s""",(self.template, self.rate, self.item)) def create_item_from_template(doc): - if(doc.is_billable == 1): + disabled = 1 + + if doc.is_billable: disabled = 0 - else: - disabled = 1 + #insert item item = frappe.get_doc({ - "doctype": "Item", - "item_code": doc.template, - "item_name":doc.template, - "item_group": doc.item_group, - "description":doc.description, - "is_sales_item": 1, - "is_service_item": 1, - "is_purchase_item": 0, - "is_stock_item": 0, - "show_in_website": 0, - "is_pro_applicable": 0, - "disabled": disabled, - "stock_uom": "Unit" + 'doctype': 'Item', + 'item_code': doc.template, + 'item_name':doc.template, + 'item_group': doc.item_group, + 'description':doc.description, + 'is_sales_item': 1, + 'is_service_item': 1, + 'is_purchase_item': 0, + 'is_stock_item': 0, + 'show_in_website': 0, + 'is_pro_applicable': 0, + 'disabled': disabled, + 'stock_uom': 'Unit' }).insert(ignore_permissions=True) #insert item price #get item price list to insert item price if(doc.rate != 0.0): - price_list_name = frappe.db.get_value("Price List", {"selling": 1}) + price_list_name = frappe.db.get_value('Price List', {'selling': 1}) if(doc.rate): make_item_price(item.name, price_list_name, doc.rate) else: make_item_price(item.name, price_list_name, 0.0) #Set item to the template - frappe.db.set_value("Clinical Procedure Template", doc.name, "item", item.name) + frappe.db.set_value('Clinical Procedure Template', doc.name, 'item', item.name) doc.reload() #refresh the doc after insert. def make_item_price(item, price_list_name, item_price): frappe.get_doc({ - "doctype": "Item Price", - "price_list": price_list_name, - "item_code": item, - "price_list_rate": item_price + 'doctype': 'Item Price', + 'price_list': price_list_name, + 'item_code': item, + 'price_list_rate': item_price }).insert(ignore_permissions=True) @frappe.whitelist() @@ -111,20 +122,11 @@ def change_item_code_from_template(item_code, doc): doc = frappe._dict(args) if(frappe.db.exists({ - "doctype": "Item", - "item_code": item_code})): - frappe.throw(_("Code {0} already exist").format(item_code)) + 'doctype': 'Item', + 'item_code': item_code})): + frappe.throw(_('Code {0} already exist').format(item_code)) else: - frappe.rename_doc("Item", doc.item_code, item_code, ignore_permissions = True) - frappe.db.set_value("Clinical Procedure Template", doc.name, "item_code", item_code) + frappe.rename_doc('Item', doc.item_code, item_code, ignore_permissions=True) + frappe.db.set_value('Clinical Procedure Template', doc.name, 'item_code', item_code) return -@frappe.whitelist() -def disable_enable_template(status, name, item_code): - frappe.db.set_value("Clinical Procedure Template", name, "disabled", status) - if (frappe.db.exists({ #set Item's disabled field to status - "doctype": "Item", - "item_code": item_code})): - frappe.db.set_value("Item", item_code, "disabled", status) - - return diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json index f6521183097..6fdc21d388f 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json @@ -78,6 +78,7 @@ }, { "default": "0", + "description": "Checking this will create new Patients with a Disabled status by default and will only be enabled after invoicing the Registration Fee.", "fieldname": "collect_registration_fee", "fieldtype": "Check", "label": "Collect Fee for Patient Registration" diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 86094896154..85db0dbabb6 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -290,19 +290,23 @@ def insert_lab_test_to_medical_record(doc): comment = "" if item.lab_test_comment: comment = str(item.lab_test_comment) - event = "" + table_row = item.lab_test_name + if item.lab_test_event: - event = item.lab_test_event - table_row = item.lab_test_name +" "+ event +" "+ item.result_value + table_row += " " + item.lab_test_event + + if item.result_value: + table_row += " " + item.result_value + if item.normal_range: table_row += " normal_range("+item.normal_range+")" - table_row += " "+comment + table_row += " " + comment elif doc.special_test_items: item = doc.special_test_items[0] if item.lab_test_particulars and item.result_value: - table_row = item.lab_test_particulars +" "+ item.result_value + table_row = item.lab_test_particulars + " " + item.result_value elif doc.sensitivity_test_items: item = doc.sensitivity_test_items[0] diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py index 89f2defb14b..5c9e7524c1a 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe, json from frappe.model.document import Document +from frappe.model.rename_doc import rename_doc from frappe import _ class LabTestTemplate(Document): @@ -98,13 +99,11 @@ def create_item_from_template(doc): # get item price list to insert item price if doc.lab_test_rate != 0.0: price_list_name = frappe.db.get_value("Price List", {"selling": 1}) - if(doc.lab_test_rate): + if doc.lab_test_rate: make_item_price(item.name, price_list_name, doc.lab_test_rate) - item.standard_rate = doc.lab_test_rate else: make_item_price(item.name, price_list_name, 0.0) - item.standard_rate = 0.0 - item.save(ignore_permissions = True) + # Set item in the template frappe.db.set_value("Lab Test Template", doc.name, "item", item.name) diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js index 1a34fe8076f..9984b0ae9c9 100644 --- a/erpnext/healthcare/doctype/patient/patient.js +++ b/erpnext/healthcare/doctype/patient/patient.js @@ -15,7 +15,7 @@ frappe.ui.form.on('Patient', { } else { erpnext.toggle_naming_series(); } - if (frappe.defaults.get_default("collect_registration_fee") && frm.doc.disabled == 1) { + if (frappe.defaults.get_default("collect_registration_fee") && frm.doc.status == 'Disabled') { frm.add_custom_button(__('Invoice Patient Registration'), function () { btn_invoice_registration(frm); }); diff --git a/erpnext/healthcare/doctype/patient/patient.json b/erpnext/healthcare/doctype/patient/patient.json index 0136f72f5be..7f97e17934f 100644 --- a/erpnext/healthcare/doctype/patient/patient.json +++ b/erpnext/healthcare/doctype/patient/patient.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_copy": 1, "allow_import": 1, "allow_rename": 1, @@ -19,15 +20,14 @@ "blood_group", "dob", "age_html", - "status", "image", "column_break_14", + "status", "customer", "report_preference", "mobile", "email", "phone", - "disabled", "sb_relation", "patient_relation", "allergy_medical_and_surgical_history", @@ -125,15 +125,15 @@ "report_hide": 1 }, { - "default": "Active", "fieldname": "status", "fieldtype": "Select", - "hidden": 1, + "in_filter": 1, + "in_list_view": 1, "label": "Status", "no_copy": 1, - "options": "Active\nDormant\nOpen", + "options": "Active\nDisabled", "print_hide": 1, - "report_hide": 1 + "read_only": 1 }, { "fieldname": "image", @@ -183,19 +183,8 @@ "fieldname": "phone", "fieldtype": "Data", "in_filter": 1, - "in_list_view": 1, "label": "Phone" }, - { - "default": "0", - "fieldname": "disabled", - "fieldtype": "Check", - "hidden": 1, - "label": "Disabled", - "no_copy": 1, - "print_hide": 1, - "report_hide": 1 - }, { "collapsible": 1, "fieldname": "sb_relation", @@ -346,8 +335,9 @@ ], "icon": "fa fa-user", "image_field": "image", + "links": [], "max_attachments": 50, - "modified": "2019-09-25 23:30:49.905893", + "modified": "2020-01-29 11:22:40.698125", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient", diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py index e3eea96f859..420416809dd 100644 --- a/erpnext/healthcare/doctype/patient/patient.py +++ b/erpnext/healthcare/doctype/patient/patient.py @@ -15,8 +15,8 @@ class Patient(Document): def after_insert(self): if(frappe.db.get_value("Healthcare Settings", None, "manage_customer") == '1' and not self.customer): create_customer(self) - if(frappe.db.get_value("Healthcare Settings", None, "collect_registration_fee") == '1'): - frappe.db.set_value("Patient", self.name, "disabled", 1) + if frappe.db.get_single_value('Healthcare Settings', 'collect_registration_fee'): + frappe.db.set_value('Patient', self.name, 'status', 'Disabled') else: send_registration_sms(self) self.reload() @@ -62,7 +62,7 @@ class Patient(Document): return age_str def invoice_patient_registration(self): - frappe.db.set_value("Patient", self.name, "disabled", 0) + frappe.db.set_value("Patient", self.name, "status", "Active") send_registration_sms(self) if(flt(frappe.get_value("Healthcare Settings", None, "registration_fee"))>0): company = frappe.defaults.get_user_default('company') diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 858145eef3c..087f1628404 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -11,7 +11,7 @@ frappe.ui.form.on('Patient Appointment', { refresh: function(frm) { frm.set_query("patient", function () { return { - filters: {"disabled": 0} + filters: {"status": "Active"} }; }); frm.set_query("practitioner", function() { @@ -288,12 +288,19 @@ var check_and_set_availability = function(frm) { }; var get_procedure_prescribed = function(frm){ - if(frm.doc.patient){ + if (frm.doc.patient) { frappe.call({ method:"erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_procedure_prescribed", args: {patient: frm.doc.patient}, - callback: function(r){ - show_procedure_templates(frm, r.message); + callback: function(r) { + if (r.message && r.message.length) { + show_procedure_templates(frm, r.message); + } else { + frappe.msgprint({ + title: __('Not Found'), + message: __('No Prescribed Procedures found for the selected Patient') + }); + } } }); } diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index ee9f013084b..93124b4de56 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -1,1177 +1,333 @@ { "allow_copy": 1, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, "allow_import": 1, - "allow_rename": 0, "autoname": "OP-.######", "beta": 1, "creation": "2017-05-04 11:52:40.941507", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Document", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "inpatient_record", + "patient", + "appointment_type", + "duration", + "column_break_1", + "status", + "procedure_template", + "get_procedure_from_encounter", + "procedure_prescription", + "service_unit", + "check_availability", + "section_break_12", + "practitioner", + "department", + "column_break_17", + "appointment_date", + "appointment_time", + "section_break_16", + "patient_name", + "patient_sex", + "column_break_21", + "patient_age", + "section_break_1", + "appointment_datetime", + "mode_of_payment", + "paid_amount", + "column_break_2", + "invoiced", + "company", + "section_break_3", + "notes", + "referring_practitioner", + "reminded" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "patient.inpatient_record", "fieldname": "inpatient_record", "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": "Inpatient Record", - "length": 0, - "no_copy": 0, "options": "Inpatient Record", - "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, "fetch_from": "inpatient_record.patient", "fieldname": "patient", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Patient", - "length": 0, - "no_copy": 0, "options": "Patient", - "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": 1, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "appointment_type", "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": "Appointment Type", - "length": 0, - "no_copy": 0, "options": "Appointment Type", - "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": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "In Minutes", "fieldname": "duration", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Duration", - "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": "Duration" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_1", "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "options": "", - "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, "default": "Scheduled", "depends_on": "eval:!doc.__islocal", "fieldname": "status", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Status", - "length": 0, - "no_copy": 0, "options": "\nScheduled\nOpen\nClosed\nPending\nCancelled", - "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": 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": "procedure_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": "Procedure", - "length": 0, - "no_copy": 0, "options": "Clinical Procedure 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": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "get_procedure_from_encounter", "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": "Get prescribed procedures", - "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 prescribed procedures" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "procedure_prescription", "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": "Procedure Prescription", - "length": 0, "no_copy": 1, "options": "Procedure Prescription", - "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 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "service_unit", "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": "Service Unit", - "length": 0, - "no_copy": 0, "options": "Healthcare Service Unit", - "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": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.__islocal", "fieldname": "check_availability", "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": "Check availability", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Check availability" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_12", - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "practitioner", "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": 1, "label": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, "search_index": 1, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "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": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Department", - "length": 0, - "no_copy": 0, "options": "Medical Department", - "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": 1, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "appointment_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": 1, "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 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": "appointment_time", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Time", - "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": "section_break_16", - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", "fetch_from": "patient.patient_name", "fieldname": "patient_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": "Patient Name", - "length": 0, - "no_copy": 0, - "options": "", - "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, "fetch_from": "patient.sex", "fieldname": "patient_sex", "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": "Gender", - "length": 0, "no_copy": 1, - "options": "", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_21", - "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": "patient_age", "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": "Patient Age", - "length": 0, - "no_copy": 0, - "options": "", - "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, - "depends_on": "", "fieldname": "section_break_1", "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": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "appointment_datetime", "fieldtype": "Datetime", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date TIme", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", + "label": "Appointment Datetime", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1, + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "mode_of_payment", "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": "Mode of Payment", - "length": 0, - "no_copy": 0, - "options": "Mode of Payment", - "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": "Mode of Payment" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "paid_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": "Paid Amount", - "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": "Paid Amount" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, "default": "0", "fieldname": "invoiced", "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": "Invoiced", - "length": 0, - "no_copy": 0, - "options": "", - "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": 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": "company", "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": "Company", - "length": 0, "no_copy": 1, "options": "Company", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "section_break_3", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Info", - "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": "More Info" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 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": 1, - "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, - "translatable": 0, - "unique": 0 + "label": "Notes" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "referring_practitioner", "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": "Referring Practitioner", - "length": 0, - "no_copy": 0, "options": "Healthcare Practitioner", - "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": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", "fieldname": "reminded", "fieldtype": "Check", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Reminded", - "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": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-14 13:24:01.529536", - "modified_by": "Administrator", + "modified": "2020-03-27 15:55:24.344945", + "modified_by": "ruchamahabal2@gmail.com", "module": "Healthcare", "name": "Patient Appointment", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Healthcare Administrator", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "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": "Physician", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "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": "Nursing User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Healthcare", "search_fields": "patient, practitioner, department, appointment_date, appointment_time", "show_name_in_global_search": 1, @@ -1179,6 +335,5 @@ "sort_order": "DESC", "title_field": "patient", "track_changes": 1, - "track_seen": 1, - "track_views": 0 -} + "track_seen": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 12248fcdce3..8f3602af67e 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -25,6 +25,7 @@ class PatientAppointment(Document): self.reload() def validate(self): + self.set_appointment_datetime() end_time = datetime.datetime.combine(getdate(self.appointment_date), get_time(self.appointment_time)) + datetime.timedelta(minutes=float(self.duration)) overlaps = frappe.db.sql(""" select @@ -44,6 +45,9 @@ class PatientAppointment(Document): frappe.throw(_("""Appointment overlaps with {0}.
{1} has appointment scheduled with {2} at {3} having {4} minute(s) duration.""").format(overlaps[0][0], overlaps[0][1], overlaps[0][2], overlaps[0][3], overlaps[0][4])) + def set_appointment_datetime(self): + self.appointment_datetime = "%s %s" % (self.appointment_date, self.appointment_time or "00:00:00") + def after_insert(self): if self.procedure_prescription: frappe.db.set_value("Procedure Prescription", self.procedure_prescription, "appointment_booked", True) @@ -319,27 +323,29 @@ def create_encounter(appointment): return encounter.as_dict() -def remind_appointment(): - if frappe.db.get_value("Healthcare Settings", None, "app_rem") == '1': - rem_before = datetime.datetime.strptime(frappe.get_value("Healthcare Settings", None, "rem_before"), "%H:%M:%S") - rem_dt = datetime.datetime.now() + datetime.timedelta( - hours=rem_before.hour, minutes=rem_before.minute, seconds=rem_before.second) +def set_appointment_reminder(): + if frappe.db.get_single_value("Healthcare Settings", "app_rem"): + remind_before = datetime.datetime.strptime(frappe.db.get_single_value("Healthcare Settings", "rem_before"), '%H:%M:%S') - appointment_list = frappe.db.sql( - "select name from `tabPatient Appointment` where start_dt between %s and %s and reminded = 0 ", - (datetime.datetime.now(), rem_dt) - ) + reminder_dt = datetime.datetime.now() + datetime.timedelta( + hours=remind_before.hour, minutes=remind_before.minute, seconds=remind_before.second) - for i in range(0, len(appointment_list)): - doc = frappe.get_doc("Patient Appointment", appointment_list[i][0]) - message = frappe.db.get_value("Healthcare Settings", None, "app_rem_msg") + appointment_list = frappe.db.get_all("Patient Appointment", { + "appointment_datetime": ["between", (datetime.datetime.now(), reminder_dt)], + "reminded": 0, + "status": ["!=", "Cancelled"] + }) + + for appointment in appointment_list: + doc = frappe.get_doc('Patient Appointment', appointment.name) + message = frappe.db.get_single_value("Healthcare Settings", "app_rem_msg") send_message(doc, message) - frappe.db.set_value("Patient Appointment", doc.name, "reminded",1) + frappe.db.set_value('Patient Appointment', doc.name, 'reminded', 1) def send_message(doc, message): - patient = frappe.get_doc("Patient", doc.patient) - if patient.mobile: + patient_mobile = frappe.db.get_value("Patient", doc.patient, "mobile") + if patient_mobile: context = {"doc": doc, "alert": doc, "comments": None} if doc.get("_comments"): context["comments"] = json.loads(doc.get("_comments")) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 088bc8161b1..c1b0b18dba4 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -62,7 +62,7 @@ frappe.ui.form.on('Patient Encounter', { frm.set_query("patient", function () { return { - filters: {"disabled": 0} + filters: {"status": "Active"} }; }); frm.set_query("drug_code", "drug_prescription", function() { diff --git a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json index 08a1b86969c..cff100cc704 100644 --- a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json +++ b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json @@ -1,160 +1,71 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:schedule_name", - "beta": 1, - "creation": "2017-05-03 17:28:03.926787", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:schedule_name", + "beta": 1, + "creation": "2017-05-03 17:28:03.926787", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "disabled", + "schedule_details_section", + "schedule_name", + "time_slots" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "schedule_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Schedule 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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "schedule_name", + "fieldtype": "Data", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Schedule Name", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "time_slots", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Time Slots", - "length": 0, - "no_copy": 0, - "options": "Healthcare Schedule Time Slot", - "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": "time_slots", + "fieldtype": "Table", + "label": "Time Slots", + "options": "Healthcare Schedule Time Slot" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 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": 0, - "in_standard_filter": 0, - "label": "Disabled", - "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": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled", + "print_hide": 1 + }, + { + "fieldname": "schedule_details_section", + "fieldtype": "Section Break", + "label": "Schedule Details" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-06-29 14:55:34.795995", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Practitioner Schedule", - "name_case": "", - "owner": "rmehta@gmail.com", + ], + "links": [], + "modified": "2020-01-31 12:21:45.975488", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Practitioner Schedule", + "owner": "rmehta@gmail.com", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "restrict_to_domain": "Healthcare", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 7f268d6ba14..81fb40506b6 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -268,7 +268,9 @@ doc_events = { scheduler_events = { "all": [ - "erpnext.projects.doctype.project.project.project_status_update_reminder" + "erpnext.projects.doctype.project.project.project_status_update_reminder", + "erpnext.healthcare_healthcare.doctype.patient_appointment.patient_appointment.send_appointment_reminder" + ], "hourly": [ 'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails', From e4cb523181ce36250b090ffd720c90059649a220 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 31 Mar 2020 15:26:24 +0530 Subject: [PATCH 263/929] fix: serial no scan not adding the serial nos in stock entry (#21082) --- erpnext/public/js/controllers/transaction.js | 7 ++++++- .../stock/doctype/stock_entry/stock_entry.js | 17 +++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index ee8ca0d5662..e1c38a91297 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -362,12 +362,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ ['serial_no', 'batch_no', 'barcode'].forEach(field => { if (data[field] && frappe.meta.has_field(row_to_modify.doctype, field)) { + + let value = (row_to_modify[field] && field === "serial_no") + ? row_to_modify[field] + '\n' + data[field] : data[field]; + frappe.model.set_value(row_to_modify.doctype, - row_to_modify.name, field, data[field]); + row_to_modify.name, field, value); } }); scan_barcode_field.set_value(''); + refresh_field("items"); }); } return false; diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 23ee9f472b8..b54233f4585 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -303,12 +303,12 @@ frappe.ui.form.on('Stock Entry', { method: "erpnext.stock.get_item_details.get_serial_no", args: {"args": args}, callback: function(r) { - if (!r.exe){ + if (!r.exe && r.message){ frappe.model.set_value(cdt, cdn, "serial_no", r.message); - } - if (callback) { - callback(); + if (callback) { + callback(); + } } } }); @@ -537,10 +537,15 @@ frappe.ui.form.on('Stock Entry Detail', { if(r.message) { var d = locals[cdt][cdn]; $.each(r.message, function(k, v) { - frappe.model.set_value(cdt, cdn, k, v); // qty and it's subsequent fields weren't triggered + if (v) { + frappe.model.set_value(cdt, cdn, k, v); // qty and it's subsequent fields weren't triggered + } }); refresh_field("items"); - erpnext.stock.select_batch_and_serial_no(frm, d); + + if (!d.serial_no) { + erpnext.stock.select_batch_and_serial_no(frm, d); + } } } }); From c5a3bcca70fa2657064c55408ed04f6828348e14 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 31 Mar 2020 15:28:15 +0530 Subject: [PATCH 264/929] fix: make for quantity as non mandatory in job card (#21080) --- erpnext/manufacturing/doctype/bom/bom.js | 1 + .../doctype/job_card/job_card.js | 14 ++- .../doctype/job_card/job_card.json | 111 ++++++++---------- .../doctype/job_card/job_card.py | 38 +++--- .../doctype/work_order/work_order.py | 3 +- 5 files changed, 80 insertions(+), 87 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 3acaee4ffbd..4f08bbc3fc7 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -135,6 +135,7 @@ frappe.ui.form.on("BOM", { frappe.call({ method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", args: { + bom_no: frm.doc.name, item: frm.doc.item, qty: data.qty || 0.0, project: frm.doc.project diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index 6db8f2f5b38..9ee5e80f88d 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -24,7 +24,7 @@ frappe.ui.form.on('Job Card', { } } - if (frm.doc.docstatus == 0 && frm.doc.for_quantity > frm.doc.total_completed_qty + if (frm.doc.docstatus == 0 && (frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity) && (!frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) { frm.trigger("prepare_timer_buttons"); } @@ -63,10 +63,14 @@ frappe.ui.form.on('Job Card', { let completed_time = frappe.datetime.now_datetime(); frm.trigger("hide_timer"); - frappe.prompt({fieldtype: 'Float', label: __('Completed Quantity'), - fieldname: 'qty', reqd: 1, default: frm.doc.for_quantity}, data => { - frm.events.complete_job(frm, completed_time, data.qty); - }, __("Enter Value"), __("Complete")); + if (frm.doc.for_quantity) { + frappe.prompt({fieldtype: 'Float', label: __('Completed Quantity'), + fieldname: 'qty', reqd: 1, default: frm.doc.for_quantity}, data => { + frm.events.complete_job(frm, completed_time, data.qty); + }, __("Enter Value"), __("Complete")); + } else { + frm.events.complete_job(frm, completed_time, 0); + } }).addClass("btn-primary"); } }, diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json index fd39af1faad..7661fffa864 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.json +++ b/erpnext/manufacturing/doctype/job_card/job_card.json @@ -99,8 +99,7 @@ "fieldname": "for_quantity", "fieldtype": "Float", "in_list_view": 1, - "label": "Qty To Manufacture", - "reqd": 1 + "label": "Qty To Manufacture" }, { "fieldname": "wip_warehouse", @@ -122,6 +121,7 @@ "options": "Employee" }, { + "allow_bulk_edit": 1, "fieldname": "time_logs", "fieldtype": "Table", "label": "Time Logs", @@ -290,78 +290,61 @@ } ], "is_submittable": 1, - "modified": "2020-03-24 13:08:57.926201", + "modified": "2020-03-27 13:36:35.417502", "modified_by": "Administrator", "module": "Manufacturing", "name": "Job Card", "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing Manager", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "operation", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "operation", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index eec7d242871..7c648c79014 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -92,10 +92,7 @@ class JobCard(Document): if not self.time_logs: frappe.throw(_("Time logs are required for job card {0}").format(self.name)) - if self.total_completed_qty <= 0.0: - frappe.throw(_("Total completed qty must be greater than zero")) - - if self.total_completed_qty != self.for_quantity: + if self.for_quantity and self.total_completed_qty != self.for_quantity: frappe.throw(_("The total completed qty({0}) must be equal to qty to manufacture({1})" .format(frappe.bold(self.total_completed_qty),frappe.bold(self.for_quantity)))) @@ -106,27 +103,34 @@ class JobCard(Document): for_quantity, time_in_mins = 0, 0 from_time_list, to_time_list = [], [] - for d in frappe.get_all('Job Card', - filters = {'docstatus': 1, 'operation_id': self.operation_id}): - doc = frappe.get_doc('Job Card', d.name) - for_quantity += doc.total_completed_qty - time_in_mins += doc.total_time_in_mins - for time_log in doc.time_logs: - if time_log.from_time: - from_time_list.append(time_log.from_time) - if time_log.to_time: - to_time_list.append(time_log.to_time) + data = frappe.get_all('Job Card', + fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"], + filters = {"docstatus": 1, "work_order": self.work_order, + "workstation": self.workstation, "operation": self.operation}) + + if data and len(data) > 0: + for_quantity = data[0].completed_qty + time_in_mins = data[0].time_in_mins if for_quantity: + time_data = frappe.db.sql(""" + SELECT + min(from_time) as start_time, max(to_time) as end_time + FROM `tabJob Card` jc, `tabJob Card Time Log` jctl + WHERE + jctl.parent = jc.name and jc.work_order = %s + and jc.workstation = %s and jc.operation = %s and jc.docstatus = 1 + """, (self.work_order, self.workstation, self.operation), as_dict=1) + wo = frappe.get_doc('Work Order', self.work_order) for data in wo.operations: - if data.name == self.operation_id: + if data.workstation == self.workstation and data.operation == self.operation: data.completed_qty = for_quantity data.actual_operation_time = time_in_mins - data.actual_start_time = min(from_time_list) if from_time_list else None - data.actual_end_time = max(to_time_list) if to_time_list else None + data.actual_start_time = time_data[0].start_time if time_data else None + data.actual_end_time = time_data[0].end_time if time_data else None wo.flags.ignore_validate_update_after_submit = True wo.update_operation_status() diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index bd2ae6c36b1..8a39f7420e9 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -609,7 +609,7 @@ def get_item_details(item, project = None): return res @frappe.whitelist() -def make_work_order(item, qty=0, project=None): +def make_work_order(bom_no, item, qty=0, project=None): if not frappe.has_permission("Work Order", "write"): frappe.throw(_("Not permitted"), frappe.PermissionError) @@ -618,6 +618,7 @@ def make_work_order(item, qty=0, project=None): wo_doc = frappe.new_doc("Work Order") wo_doc.production_item = item wo_doc.update(item_details) + wo_doc.bom_no = bom_no if flt(qty) > 0: wo_doc.qty = flt(qty) From 798d532a11bf93f7a8e393308e4de425c2fb7d14 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 31 Mar 2020 15:30:53 +0530 Subject: [PATCH 265/929] =?UTF-8?q?fix:=20warehouse=5Faccount=5Fmap=20not?= =?UTF-8?q?=20getting=20reset=20for=20diff=20company=20transac=E2=80=A6=20?= =?UTF-8?q?(#20997)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: warehouse_account_map not getting reset for diff company transaction * fix: potential key errors while fetching warehouse_account_map * fix: travis Co-authored-by: Nabin Hait --- erpnext/stock/__init__.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/__init__.py b/erpnext/stock/__init__.py index a4d4cbd8ef8..8d64efe41dd 100644 --- a/erpnext/stock/__init__.py +++ b/erpnext/stock/__init__.py @@ -13,12 +13,16 @@ install_docs = [ ] def get_warehouse_account_map(company=None): - if not frappe.flags.warehouse_account_map or frappe.flags.in_test: + company_warehouse_account_map = company and frappe.flags.setdefault('warehouse_account_map', {}).get(company) + warehouse_account_map = frappe.flags.warehouse_account_map + + if not warehouse_account_map or not company_warehouse_account_map or frappe.flags.in_test: warehouse_account = frappe._dict() filters = {} if company: filters['company'] = company + frappe.flags.setdefault('warehouse_account_map', {}).setdefault(company, {}) for d in frappe.get_all('Warehouse', fields = ["name", "account", "parent_warehouse", "company", "is_group"], @@ -30,10 +34,12 @@ def get_warehouse_account_map(company=None): if d.account: d.account_currency = frappe.db.get_value('Account', d.account, 'account_currency', cache=True) warehouse_account.setdefault(d.name, d) - - frappe.flags.warehouse_account_map = warehouse_account - - return frappe.flags.warehouse_account_map + if company: + frappe.flags.warehouse_account_map[company] = warehouse_account + else: + frappe.flags.warehouse_account_map = warehouse_account + + return frappe.flags.warehouse_account_map.get(company) or frappe.flags.warehouse_account_map def get_warehouse_account(warehouse, warehouse_account=None): account = warehouse.account From 086e5c4dac235ef99775488fbd0d497ff23b54f2 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sat, 28 Mar 2020 18:44:53 +0530 Subject: [PATCH 266/929] fix: no server side validations for accounts in asset category --- erpnext/assets/doctype/asset_category/asset_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py index 855c7094115..e9ef0c7d9ee 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -31,7 +31,7 @@ class AssetCategory(Document): if d.get(account): account_type = frappe.db.get_value('Account', d.get(account), 'account_type') if account_type != account_type_map[account]['account_type']: - frappe.throw(_("Row {}: {} should be a {} account".format(d.idx, frappe.bold(frappe.unscrub(account)), + frappe.throw(_("Row {}: Account Type of {} should be {} account".format(d.idx, frappe.bold(frappe.unscrub(account)), frappe.bold(account_type_map[account]['account_type']))), title=_("Invalid Account")) From 0541b2ee27280f79c61a4b2a6a42a851486c7ae3 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 31 Mar 2020 18:44:02 +0530 Subject: [PATCH 267/929] fix: travis --- erpnext/accounts/doctype/payment_request/payment_request.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index b18a938fbb0..9c74de4af61 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -424,12 +424,11 @@ def make_status_as_paid(doc, method): "docstatus": 1}) if payment_request_name: - outstanding_amt = frappe.get_value(ref.reference_doctype, ref.reference_name, 'outstanding_amount') doc = frappe.get_doc("Payment Request", payment_request_name) - if doc.status != "Paid" and outstanding_amt <= 0: + if doc.status != "Paid" and ref.outstanding_amount <= ref.allocated_amount: doc.db_set('status', 'Paid') frappe.db.commit() - elif doc.status != "Partially Paid" and outstanding_amt != doc.grand_total: + elif doc.status != "Partially Paid" and ref.outstanding_amount != ref.allocated_amount: doc.db_set('status', 'Partially Paid') frappe.db.commit() From 954276ff0e65075af239434c51fa576d8b1e3dc3 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 31 Mar 2020 18:48:35 +0530 Subject: [PATCH 268/929] fix: check if selling price exists then set it --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 919af057ac1..eda80246cdb 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -438,7 +438,8 @@ class SalesInvoice(SellingController): selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list') - self.set('selling_price_list', selling_price_list) + if selling_price_list: + self.set('selling_price_list', selling_price_list) if not for_validate: self.update_stock = cint(pos.get("update_stock")) From 122049a9b4aa3902b27d222a2c80b17b7c9e201d Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 1 Apr 2020 11:04:14 +0530 Subject: [PATCH 269/929] fix: Typo in stock level validation in Stock Ledger --- erpnext/stock/stock_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index b100f453273..7567a1ae758 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -428,7 +428,7 @@ class update_entries_after(object): frappe.get_desk_link(self.exceptions[0]["voucher_type"], self.exceptions[0]["voucher_no"])) if self.verbose: - frappe.throw(msg, NegativeStockError, title='Insufficent Stock') + frappe.throw(msg, NegativeStockError, title='Insufficient Stock') else: raise NegativeStockError(msg) From a66da0ddf4f6d3d749a65d628efb7444b596cb24 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Thu, 2 Apr 2020 01:06:30 +0530 Subject: [PATCH 270/929] Sum of years not needed. --- .../customer_acquisition_and_loyalty.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py index 4509904249f..aabc503a1ba 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py @@ -48,7 +48,7 @@ def execute(filters=None): new = new_customers_in.get(key, [0,0.0]) repeat = repeat_customers_in.get(key, [0,0.0]) - out.append([year, calendar.month_name[month], + out.append([str(year), calendar.month_name[month], new[0], repeat[0], new[0] + repeat[0], new[1], repeat[1], new[1] + repeat[1]]) From 29fc06376496be58b24b655c19856d52ba4ad328 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Thu, 2 Apr 2020 11:34:42 +0530 Subject: [PATCH 271/929] Sum of years not needed. --- .../customer_acquisition_and_loyalty.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py index aabc503a1ba..28dd0564075 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import getdate, cint +from frappe.utils import getdate, cint, cstr import calendar def execute(filters=None): @@ -48,7 +48,7 @@ def execute(filters=None): new = new_customers_in.get(key, [0,0.0]) repeat = repeat_customers_in.get(key, [0,0.0]) - out.append([str(year), calendar.month_name[month], + out.append([cstr(year), calendar.month_name[month], new[0], repeat[0], new[0] + repeat[0], new[1], repeat[1], new[1] + repeat[1]]) From 763aab330126267c443da6453e4b3ea3120f6333 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 2 Apr 2020 19:12:36 +0530 Subject: [PATCH 272/929] fix: Travis --- .../doctype/purchase_invoice_item/purchase_invoice_item.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 36bb2be3dc5..296c7049ce4 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -760,7 +760,7 @@ "depends_on": "is_fixed_asset", "fetch_from": "item_code.asset_category", "fieldname": "asset_category", - "fieldtype": "Data", + "fieldtype": "Link", "in_preview": 1, "label": "Asset Category", "options": "Asset Category", @@ -770,7 +770,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-05 14:20:17.297284", + "modified": "2020-04-01 14:20:17.297284", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", From 4a59b1d3b5298aafc03bebb22f7ec3a0596dd6eb Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 2 Apr 2020 19:36:21 +0530 Subject: [PATCH 273/929] fix: Fixed Test Cases for Material Request - Reduce quantity on Material Transfer/Issue - No effect on Customer Provided Material Request --- .../material_request/test_material_request.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 79cdc1ad18a..b925aedd1ac 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -276,8 +276,8 @@ class TestMaterialRequest(unittest.TestCase): current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC") current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC") - self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0) - self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0) + self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 54.0) + self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0) from erpnext.stock.doctype.material_request.material_request import make_stock_entry @@ -331,8 +331,8 @@ class TestMaterialRequest(unittest.TestCase): current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC") current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC") - self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 27.0) - self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 1.5) + self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 27.0) + self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 1.5) # check if per complete is as expected for Stock Entry cancelled se.cancel() @@ -344,8 +344,8 @@ class TestMaterialRequest(unittest.TestCase): current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC") current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC") - self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0) - self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0) + self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 54.0) + self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0) def test_completed_qty_for_over_transfer(self): existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC") @@ -425,8 +425,8 @@ class TestMaterialRequest(unittest.TestCase): current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC") current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC") - self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0) - self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0) + self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 54.0) + self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0) def test_incorrect_mapping_of_stock_entry(self): # submit material request of type Transfer @@ -512,7 +512,7 @@ class TestMaterialRequest(unittest.TestCase): mr.submit() #testing bin value after material request is submitted - self.assertEqual(_get_requested_qty(), existing_requested_qty + 54.0) + self.assertEqual(_get_requested_qty(), existing_requested_qty - 54.0) # receive items to allow issue self._insert_stock_entry(60, 6, "_Test Warehouse - _TC") @@ -609,6 +609,8 @@ class TestMaterialRequest(unittest.TestCase): def test_customer_provided_parts_mr(self): from erpnext.stock.doctype.material_request.material_request import make_stock_entry create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0) + existing_requested_qty = self._get_requested_qty("_Test Customer", "_Test Warehouse - _TC") + mr = make_material_request(item_code='CUST-0987', material_request_type='Customer Provided') se = make_stock_entry(mr.name) se.insert() @@ -617,7 +619,10 @@ class TestMaterialRequest(unittest.TestCase): self.assertEqual(se.get("items")[0].material_request, mr.name) mr = frappe.get_doc("Material Request", mr.name) mr.submit() + current_requested_qty = self._get_requested_qty("_Test Customer", "_Test Warehouse - _TC") + self.assertEqual(mr.per_ordered, 100) + self.assertEqual(existing_requested_qty, current_requested_qty) def make_material_request(**args): args = frappe._dict(args) From f8cb81c9f9c98653d1f4becff7432842225b6e83 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 2 Apr 2020 19:57:11 +0530 Subject: [PATCH 274/929] fix (tests): reload doctype --- erpnext/patches.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index de4c89c5ace..176e309ce24 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -627,10 +627,11 @@ erpnext.patches.v12_0.update_ewaybill_field_position erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes erpnext.patches.v11_1.set_status_for_material_request_type_manufacture erpnext.patches.v12_0.move_plaid_settings_to_doctype -execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_link') -execute:frappe.reload_doc('desk', 'doctype','dashboard') -execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_source') -execute:frappe.reload_doc('desk', 'doctype','dashboard_chart') +execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_link') +execute:frappe.reload_doc('desk', 'doctype', 'dashboard') +execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_source') +execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart') +execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_field') erpnext.patches.v12_0.add_default_dashboards erpnext.patches.v12_0.remove_bank_remittance_custom_fields erpnext.patches.v12_0.generate_leave_ledger_entries From 04201028d15063901faf952c8b03dc125006efc7 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 2 Apr 2020 22:17:41 +0530 Subject: [PATCH 275/929] fix: tests --- erpnext/accounts/doctype/account/test_account.py | 5 +++-- .../doctype/asset_category/asset_category.py | 16 ++++++++++------ .../purchase_receipt/test_purchase_receipt.py | 4 ++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index dc23b2b2d05..9894b9309aa 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -69,6 +69,7 @@ class TestAccount(unittest.TestCase): acc.account_name = "Accumulated Depreciation" acc.parent_account = "Fixed Assets - _TC" acc.company = "_Test Company" + acc.account_type = "Accumulated Depreciation" acc.insert() doc = frappe.get_doc("Account", "Securities and Deposits - _TC") @@ -149,8 +150,8 @@ def _make_test_records(verbose): # fixed asset depreciation ["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None], - ["_Test Accumulated Depreciations", "Current Assets", 0, None, None], - ["_Test Depreciations", "Expenses", 0, None, None], + ["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None], + ["_Test Depreciations", "Expenses", 0, "Expense", None], ["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None], # Receivable / Payable Account diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py index e9ef0c7d9ee..85e5d98d163 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -27,12 +27,16 @@ class AssetCategory(Document): 'capital_work_in_progress_account': { 'account_type': 'Capital Work in Progress' } } for d in self.accounts: - for account in account_type_map.keys(): - if d.get(account): - account_type = frappe.db.get_value('Account', d.get(account), 'account_type') - if account_type != account_type_map[account]['account_type']: - frappe.throw(_("Row {}: Account Type of {} should be {} account".format(d.idx, frappe.bold(frappe.unscrub(account)), - frappe.bold(account_type_map[account]['account_type']))), title=_("Invalid Account")) + for fieldname in account_type_map.keys(): + if d.get(fieldname): + selected_account = d.get(fieldname) + selected_account_type = frappe.db.get_value('Account', selected_account, 'account_type') + expected_account_type = account_type_map[fieldname]['account_type'] + + if selected_account_type != expected_account_type: + frappe.throw(_("Row #{}: Account Type of {} should be {}. Please modify the account type or select a different account." + .format(d.idx, frappe.bold(selected_account), frappe.bold(expected_account_type))), + title=_("Invalid Account")) @frappe.whitelist() diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index a51b25bf36d..09271cebfa8 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -355,8 +355,8 @@ class TestPurchaseReceipt(unittest.TestCase): 'accounts': [{ 'company_name': '_Test Company', 'fixed_asset_account': '_Test Fixed Asset - _TC', - 'accumulated_depreciation_account': 'Depreciation - _TC', - 'depreciation_expense_account': 'Depreciation - _TC' + 'accumulated_depreciation_account': '_Test Accumulated Depreciations - _TC', + 'depreciation_expense_account': '_Test Depreciation - _TC' }] }).insert() From 44dbd98d90cd4726c6d5df308ec7456f73c6a746 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 3 Apr 2020 10:01:14 +0530 Subject: [PATCH 276/929] fix: cannot set warehouse on deleting all so items and updating them (#21079) * fix: cannot set warehouse on deleting all so items and updating them * fix: travis * fix: travis * fix: docname is editable in update items dialog Co-authored-by: Marica --- erpnext/controllers/accounts_controller.py | 15 +++--- erpnext/public/js/utils.js | 3 +- erpnext/stock/get_item_details.py | 60 ++++++++++++++-------- 3 files changed, 49 insertions(+), 29 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 5abe7366d0f..c17927a35c0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -19,6 +19,7 @@ from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_t from erpnext.exceptions import InvalidCurrency from six import text_type from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from erpnext.stock.get_item_details import get_item_warehouse force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules") @@ -1126,16 +1127,16 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, """ Returns a Sales Order Item child item containing the default values """ - p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name) - child_item = frappe.new_doc('Sales Order Item', p_doctype, child_docname) + p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) + child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname) item = frappe.get_doc("Item", item_code) child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description - child_item.reqd_by_date = p_doctype.delivery_date + child_item.reqd_by_date = p_doc.delivery_date child_item.uom = item.stock_uom child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 - child_item.warehouse = p_doctype.set_warehouse or p_doctype.items[0].warehouse + child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) return child_item @@ -1143,13 +1144,13 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna """ Returns a Purchase Order Item child item containing the default values """ - p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name) - child_item = frappe.new_doc('Purchase Order Item', p_doctype, child_docname) + p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) + child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname) item = frappe.get_doc("Item", item_code) child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description - child_item.schedule_date = p_doctype.schedule_date + child_item.schedule_date = p_doc.schedule_date child_item.uom = item.stock_uom child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.base_rate = 1 # Initiallize value will update in parent validation diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 98986f8bf1b..0a352648304 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -453,7 +453,8 @@ erpnext.utils.update_child_items = function(opts) { fields: [{ fieldtype:'Data', fieldname:"docname", - hidden: 0, + read_only: 1, + hidden: 1, }, { fieldtype:'Link', fieldname:"item_code", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index b76a9b064a6..d15a2597386 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -239,26 +239,13 @@ def get_basic_details(args, item, overwrite_warehouse=True): item_group_defaults = get_item_group_defaults(item.name, args.company) brand_defaults = get_brand_defaults(item.name, args.company) - if overwrite_warehouse or not args.warehouse: - warehouse = ( - args.get("set_warehouse") or - item_defaults.get("default_warehouse") or - item_group_defaults.get("default_warehouse") or - brand_defaults.get("default_warehouse") or - args.warehouse - ) - - if not warehouse: - defaults = frappe.defaults.get_defaults() or {} - warehouse_exists = frappe.db.exists("Warehouse", { - 'name': defaults.default_warehouse, - 'company': args.company - }) - if defaults.get("default_warehouse") and warehouse_exists: - warehouse = defaults.default_warehouse - - else: - warehouse = args.warehouse + defaults = frappe._dict({ + 'item_defaults': item_defaults, + 'item_group_defaults': item_group_defaults, + 'brand_defaults': brand_defaults + }) + + warehouse = get_item_warehouse(item, args, overwrite_warehouse, defaults) if args.get('doctype') == "Material Request" and not args.get('material_request_type'): args['material_request_type'] = frappe.db.get_value('Material Request', @@ -271,7 +258,7 @@ def get_basic_details(args, item, overwrite_warehouse=True): expense_account = get_asset_category_account(fieldname = "fixed_asset_account", item = args.item_code, company= args.company) #Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master - if not args.uom: + if not args.get('uom'): if args.get('doctype') in sales_doctypes: args.uom = item.sales_uom if item.sales_uom else item.stock_uom elif (args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']) or \ @@ -360,6 +347,37 @@ def get_basic_details(args, item, overwrite_warehouse=True): return out +def get_item_warehouse(item, args, overwrite_warehouse, defaults={}): + if not defaults: + defaults = frappe._dict({ + 'item_defaults' : get_item_defaults(item.name, args.company), + 'item_group_defaults' : get_item_group_defaults(item.name, args.company), + 'brand_defaults' : get_brand_defaults(item.name, args.company) + }) + + if overwrite_warehouse or not args.warehouse: + warehouse = ( + args.get("set_warehouse") or + defaults.item_defaults.get("default_warehouse") or + defaults.item_group_defaults.get("default_warehouse") or + defaults.brand_defaults.get("default_warehouse") or + args.get('warehouse') + ) + + if not warehouse: + defaults = frappe.defaults.get_defaults() or {} + warehouse_exists = frappe.db.exists("Warehouse", { + 'name': defaults.default_warehouse, + 'company': args.company + }) + if defaults.get("default_warehouse") and warehouse_exists: + warehouse = defaults.default_warehouse + + else: + warehouse = args.get('warehouse') + + return warehouse + def update_barcode_value(out): from erpnext.accounts.doctype.sales_invoice.pos import get_barcode_data barcode_data = get_barcode_data([out]) From 73bc29c011963a7f986d4879e6af565046c1816b Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 3 Apr 2020 10:06:39 +0530 Subject: [PATCH 277/929] fix: Update Received Qty in Material Request as per Stock UOM (#21055) * fix: Update Received Qty in Material Request as per Stock UOM * fix: Process each PR only once * fix: minor suggested changes Co-authored-by: Nabin Hait --- erpnext/patches.txt | 1 + ...ty_in_material_request_as_per_stock_uom.py | 30 +++++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 8 ++--- 3 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index de4c89c5ace..9deb88f8967 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -651,3 +651,4 @@ erpnext.patches.v12_0.add_export_type_field_in_party_master erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22 erpnext.patches.v12_0.create_irs_1099_field_united_states erpnext.patches.v12_0.set_permission_einvoicing +erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom \ No newline at end of file diff --git a/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py b/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py new file mode 100644 index 00000000000..88c3e2e3024 --- /dev/null +++ b/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py @@ -0,0 +1,30 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + purchase_receipts = frappe.db.sql(""" + SELECT + parent from `tabPurchase Receipt Item` + WHERE + material_request is not null + AND docstatus=1 + """,as_dict=1) + + purchase_receipts = set([d.parent for d in purchase_receipts]) + + for pr in purchase_receipts: + doc = frappe.get_doc("Purchase Receipt", pr) + doc.status_updater = [ + { + 'source_dt': 'Purchase Receipt Item', + 'target_dt': 'Material Request Item', + 'join_field': 'material_request_item', + 'target_field': 'received_qty', + 'target_parent_dt': 'Material Request', + 'target_parent_field': 'per_received', + 'target_ref_field': 'stock_qty', + 'source_field': 'stock_qty', + 'percent_join_field': 'material_request' + } + ] + doc.update_qty() diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index fb123b9c1fa..f21c4ef8593 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -49,8 +49,8 @@ class PurchaseReceipt(BuyingController): 'target_field': 'received_qty', 'target_parent_dt': 'Material Request', 'target_parent_field': 'per_received', - 'target_ref_field': 'qty', - 'source_field': 'qty', + 'target_ref_field': 'stock_qty', + 'source_field': 'stock_qty', 'percent_join_field': 'material_request' }] if cint(self.is_return): @@ -349,7 +349,7 @@ class PurchaseReceipt(BuyingController): if warehouse_with_no_account: frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" + "\n".join(warehouse_with_no_account)) - + return process_gl_map(gl_entries) def get_asset_gl_entry(self, gl_entries): @@ -616,7 +616,7 @@ def get_item_account_wise_additional_cost(purchase_document): if not landed_cost_vouchers: return - + item_account_wise_cost = {} for lcv in landed_cost_vouchers: From 878e4cb3cedb9dbcd5831ae1f5553baef498ff87 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 3 Apr 2020 10:07:44 +0530 Subject: [PATCH 278/929] fix: lms quiz type error (#21153) --- erpnext/public/js/education/lms/quiz.js | 2 +- erpnext/www/lms/content.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/education/lms/quiz.js b/erpnext/public/js/education/lms/quiz.js index 52481291e02..91cbbf4d662 100644 --- a/erpnext/public/js/education/lms/quiz.js +++ b/erpnext/public/js/education/lms/quiz.js @@ -29,7 +29,7 @@ class Quiz { this.questions.push(question) this.wrapper.appendChild(question_wrapper); }) - if (data.activity.is_complete) { + if (data.activity && data.activity.is_complete) { this.disable() let indicator = 'red' let message = 'Your are not allowed to attempt the quiz again.' diff --git a/erpnext/www/lms/content.html b/erpnext/www/lms/content.html index 5607c0814d4..cdc71412c48 100644 --- a/erpnext/www/lms/content.html +++ b/erpnext/www/lms/content.html @@ -63,7 +63,7 @@
-

{{ content.name }} ({{ position + 1 }}/{{length}})

+

{{ content.name }} ({{ position + 1 }}/{{length}})

{% endmacro %} From 7b70679bf0005aa46ac5589c7d2e699b260bfb30 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 3 Apr 2020 15:46:48 +0530 Subject: [PATCH 279/929] fix: Mapping Customer Provided Material Request to Stock Entry - Fixed inability to map Material Request to Stock Entry - Commonified Customer Provided Item validation --- erpnext/controllers/stock_controller.py | 7 +++++++ erpnext/stock/doctype/delivery_note/delivery_note.py | 7 ------- erpnext/stock/doctype/material_request/material_request.py | 5 ++++- erpnext/stock/doctype/stock_entry/stock_entry.py | 5 +---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index c6d0c7c0a6e..f5c42b49927 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -21,6 +21,7 @@ class StockController(AccountsController): super(StockController, self).validate() self.validate_inspection() self.validate_serialized_batch() + self.validate_customer_provided_item() def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False): if self.docstatus == 2: @@ -368,6 +369,12 @@ class StockController(AccountsController): for blanket_order in blanket_orders: frappe.get_doc("Blanket Order", blanket_order).update_ordered_qty() + def validate_customer_provided_item(self): + for d in self.get('items'): + # Customer Provided parts will have zero valuation rate + if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): + d.allow_zero_valuation_rate = 1 + def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, warehouse_account=None, company=None): def _delete_gl_entries(voucher_type, voucher_no): diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 3f74b54ce80..c72bb893fff 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -111,7 +111,6 @@ class DeliveryNote(SellingController): self.so_required() self.validate_proj_cust() self.check_sales_order_on_hold_or_close("against_sales_order") - self.validate_for_items() self.validate_warehouse() self.validate_uom_is_integer("stock_uom", "stock_qty") self.validate_uom_is_integer("uom", "qty") @@ -165,12 +164,6 @@ class DeliveryNote(SellingController): if not res: frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project)) - def validate_for_items(self): - for d in self.get('items'): - #Customer Provided parts will have zero valuation rate - if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): - d.allow_zero_valuation_rate = 1 - def validate_warehouse(self): super(DeliveryNote, self).validate_warehouse() diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 78383e05a6f..c11f59fd81e 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -437,6 +437,9 @@ def make_stock_entry(source_name, target_doc=None): else: target.s_warehouse = obj.warehouse + if source_parent.material_request_type == "Customer Provided": + target.allow_zero_valuation_rate = 1 + def set_missing_values(source, target): target.purpose = source.material_request_type if source.job_card: @@ -454,7 +457,7 @@ def make_stock_entry(source_name, target_doc=None): "doctype": "Stock Entry", "validation": { "docstatus": ["=", 1], - "material_request_type": ["in", ["Material Transfer", "Material Issue"]] + "material_request_type": ["in", ["Material Transfer", "Material Issue", "Customer Provided"]] } }, "Material Request Item": { diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index b2384f1b4e9..58131ea76be 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -50,6 +50,7 @@ class StockEntry(StockController): self.validate_posting_time() self.validate_purpose() self.validate_item() + self.validate_customer_provided_item() self.validate_qty() self.set_transfer_qty() self.validate_uom_is_integer("uom", "qty") @@ -203,10 +204,6 @@ class StockEntry(StockController): frappe.throw(_("Row #{0}: Please specify Serial No for Item {1}").format(item.idx, item.item_code), frappe.MandatoryError) - #Customer Provided parts will have zero valuation rate - if frappe.db.get_value('Item', item.item_code, 'is_customer_provided_item'): - item.allow_zero_valuation_rate = 1 - def validate_qty(self): manufacture_purpose = ["Manufacture", "Material Consumption for Manufacture"] From 64ed25abfb34d91a423336d13848a5096e0b059c Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 3 Apr 2020 17:17:04 +0530 Subject: [PATCH 280/929] fix: TypeError for _ --- .../chart_of_accounts_importer/chart_of_accounts_importer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7f51b139de4..00311c4f66d 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 @@ -108,7 +108,7 @@ def build_forest(data): error_messages = [] for i in data: - account_name, _, account_number, is_group, account_type, root_type = i + 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)) From dfe4d36e8d7fcc65d6445c11765a2430bb6ef997 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 3 Apr 2020 18:25:21 +0530 Subject: [PATCH 281/929] fix: Test Cases and block expense on outward entry - Throw error if rate present against Customer Provided Item in DN and SI - Added test cases for Sales Invoice and Delivery Note - Allow SI and DN with 0 rate in Test --- .../doctype/sales_invoice/test_sales_invoice.py | 12 +++++++++++- erpnext/controllers/stock_controller.py | 3 +++ .../doctype/delivery_note/test_delivery_note.py | 12 +++++++++++- .../stock/doctype/stock_entry/test_stock_entry.py | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 267e52a477b..ad0e9572497 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1868,6 +1868,16 @@ class TestSalesInvoice(unittest.TestCase): item.taxes = [] item.save() + def test_customer_provided_parts_si(self): + create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0) + si = create_sales_invoice(item_code='CUST-0987', rate=0) + self.assertEqual(si.get("items")[0].allow_zero_valuation_rate, 1) + self.assertEqual(si.get("items")[0].amount, 0) + + # test if Sales Invoice with rate is allowed + si2 = create_sales_invoice(item_code='CUST-0987', do_not_save=True) + self.assertRaises(frappe.ValidationError, si2.save) + def create_sales_invoice(**args): si = frappe.new_doc("Sales Invoice") args = frappe._dict(args) @@ -1890,7 +1900,7 @@ def create_sales_invoice(**args): "gst_hsn_code": "999800", "warehouse": args.warehouse or "_Test Warehouse - _TC", "qty": args.qty or 1, - "rate": args.rate or 100, + "rate": args.rate if args.get("rate") is not None else 100, "income_account": args.income_account or "Sales - _TC", "expense_account": args.expense_account or "Cost of Goods Sold - _TC", "cost_center": args.cost_center or "_Test Cost Center - _TC", diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index f5c42b49927..5c09068a035 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -374,6 +374,9 @@ class StockController(AccountsController): # Customer Provided parts will have zero valuation rate if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): d.allow_zero_valuation_rate = 1 + if d.parenttype in ["Delivery Note", "Sales Invoice"] and d.rate: + frappe.throw(_("Row #{0}: {1} cannot have {2} as it is a Customer Provided Item") + .format(d.idx, frappe.bold(d.item_code), frappe.bold("Rate"))) def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, warehouse_account=None, company=None): diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index dc92c5c9ff2..47a72b21a69 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -21,6 +21,7 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order, create_dn_against_so from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse +from erpnext.stock.doctype.item.test_item import create_item class TestDeliveryNote(unittest.TestCase): def setUp(self): @@ -433,6 +434,15 @@ class TestDeliveryNote(unittest.TestCase): update_delivery_note_status(dn.name, "Closed") self.assertEqual(frappe.db.get_value("Delivery Note", dn.name, "Status"), "Closed") + def test_customer_provided_parts_dn(self): + create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0) + dn = create_delivery_note(item_code='CUST-0987', rate=0) + self.assertEqual(dn.get("items")[0].allow_zero_valuation_rate, 1) + + # test if Delivery Note with rate is allowed against Customer Provided Item + dn2 = create_delivery_note(item_code='CUST-0987', do_not_save=True) + self.assertRaises(frappe.ValidationError, dn2.save) + def test_dn_billing_status_case1(self): # SO -> DN -> SI so = make_sales_order() @@ -671,7 +681,7 @@ def create_delivery_note(**args): "item_code": args.item or args.item_code or "_Test Item", "warehouse": args.warehouse or "_Test Warehouse - _TC", "qty": args.qty or 1, - "rate": args.rate or 100, + "rate": args.rate if args.get("rate") is not None else 100, "conversion_factor": 1.0, "allow_zero_valuation_rate": args.allow_zero_valuation_rate or 1, "expense_account": args.expense_account or "Cost of Goods Sold - _TC", diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index ee5f2370987..2afabe1480d 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -744,7 +744,7 @@ class TestStockEntry(unittest.TestCase): def test_customer_provided_parts_se(self): create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0) - se = make_stock_entry(item_code='CUST-0987', purporse = 'Material Receipt', qty=4, to_warehouse = "_Test Warehouse - _TC") + se = make_stock_entry(item_code='CUST-0987', purpose = 'Material Receipt', qty=4, to_warehouse = "_Test Warehouse - _TC") self.assertEqual(se.get("items")[0].allow_zero_valuation_rate, 1) self.assertEqual(se.get("items")[0].amount, 0) From dba4bd6f26c4f01d6b58f39ca51c03d0f0422ecc Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 3 Apr 2020 19:36:00 +0530 Subject: [PATCH 282/929] fix: [ux] credit to & debit to error message (#21133) Co-authored-by: Nabin Hait --- .../doctype/purchase_invoice/purchase_invoice.py | 8 ++++++-- .../accounts/doctype/sales_invoice/sales_invoice.py | 10 +++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 698c2ab333d..3933b5b1ee8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -146,10 +146,14 @@ class PurchaseInvoice(BuyingController): ["account_type", "report_type", "account_currency"], as_dict=True) if account.report_type != "Balance Sheet": - frappe.throw(_("Credit To account must be a Balance Sheet account")) + frappe.throw(_("Please ensure {} account is a Balance Sheet account. \ + You can change the parent account to a Balance Sheet account or select a different account.") + .format(frappe.bold("Credit To")), title=_("Invalid Account")) if self.supplier and account.account_type != "Payable": - frappe.throw(_("Credit To account must be a Payable account")) + frappe.throw(_("Please ensure {} account is a Payable account. \ + Change the account type to Payable or select a different account.") + .format(frappe.bold("Credit To")), title=_("Invalid Account")) self.party_account_currency = account.account_currency diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index eda80246cdb..b6d2affee54 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -470,13 +470,17 @@ class SalesInvoice(SellingController): ["account_type", "report_type", "account_currency"], as_dict=True) if not account: - frappe.throw(_("Debit To is required")) + frappe.throw(_("Debit To is required"), title=_("Account Missing")) if account.report_type != "Balance Sheet": - frappe.throw(_("Debit To account must be a Balance Sheet account")) + frappe.throw(_("Please ensure {} account is a Balance Sheet account. \ + You can change the parent account to a Balance Sheet account or select a different account.") + .format(frappe.bold("Debit To")), title=_("Invalid Account")) if self.customer and account.account_type != "Receivable": - frappe.throw(_("Debit To account must be a Receivable account")) + frappe.throw(_("Please ensure {} account is a Receivable account. \ + Change the account type to Receivable or select a different account.") + .format(frappe.bold("Debit To")), title=_("Invalid Account")) self.party_account_currency = account.account_currency From 18cd3a029dd4997d21169b709e1493a5156c94c6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 4 Apr 2020 20:05:17 +0530 Subject: [PATCH 283/929] fix: User permissions in GSTR 3B report --- erpnext/patches.txt | 2 +- .../v11_0/add_permissions_in_gst_settings.py | 7 ++----- .../gstr_3b_report/gstr_3b_report.html | 2 +- .../gstr_3b_report/gstr_3b_report.json | 19 ++++--------------- erpnext/regional/india/setup.py | 10 ++++++++-- 5 files changed, 16 insertions(+), 24 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index de4c89c5ace..3146a3a6fb7 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -582,7 +582,7 @@ erpnext.patches.v11_0.rename_bom_wo_fields erpnext.patches.v12_0.set_default_homepage_type erpnext.patches.v11_0.rename_additional_salary_component_additional_salary erpnext.patches.v11_0.renamed_from_to_fields_in_project -erpnext.patches.v11_0.add_permissions_in_gst_settings +erpnext.patches.v11_0.add_permissions_in_gst_settings #2020-04-04 erpnext.patches.v11_1.setup_guardian_role execute:frappe.delete_doc('DocType', 'Notification Control') erpnext.patches.v12_0.set_gst_category diff --git a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py index e8fcf33bede..121a20288c3 100644 --- a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py +++ b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py @@ -1,12 +1,9 @@ import frappe -from frappe.permissions import add_permission, update_permission_property +from erpnext.regional.india.setup import add_permissions def execute(): company = frappe.get_all('Company', filters = {'country': 'India'}) if not company: return - for doctype in ('GST HSN Code', 'GST Settings'): - add_permission(doctype, 'Accounts Manager', 0) - update_permission_property(doctype, 'Accounts Manager', 0, 'write', 1) - update_permission_property(doctype, 'Accounts Manager', 0, 'create', 1) \ No newline at end of file + add_permissions() \ No newline at end of file diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html index 2da79a63648..35f9cf674ce 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html @@ -29,7 +29,7 @@
- + diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.json b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.json index 548d40b9745..1f208df1f23 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.json +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "format:GSTR3B-{month}-{year}-{company_address}", "creation": "2019-02-04 11:35:55.964639", "doctype": "DocType", @@ -48,25 +49,13 @@ "read_only": 1 } ], - "modified": "2019-08-10 22:30:26.727038", + "links": [], + "modified": "2020-04-04 19:32:30.772908", "modified_by": "Administrator", "module": "Regional", "name": "GSTR 3B Report", "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], + "permissions": [], "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index cabfde40ef4..1957e7fecca 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -77,13 +77,19 @@ def add_custom_roles_for_reports(): )).insert() def add_permissions(): - for doctype in ('GST HSN Code', 'GST Settings'): + for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report'): add_permission(doctype, 'All', 0) - for role in ('Accounts Manager', 'System Manager', 'Item Manager', 'Stock Manager'): + for role in ('Accounts Manager', 'Accounts User', 'System Manager'): add_permission(doctype, role, 0) update_permission_property(doctype, role, 0, 'write', 1) update_permission_property(doctype, role, 0, 'create', 1) + if doctype == 'GST HSN Code': + for role in ('Item Manager', 'Stock Manager'): + add_permission(doctype, role, 0) + update_permission_property(doctype, role, 0, 'write', 1) + update_permission_property(doctype, role, 0, 'create', 1) + def add_print_formats(): frappe.reload_doc("regional", "print_format", "gst_tax_invoice") frappe.reload_doc("accounts", "print_format", "gst_pos_invoice") From 2378572e1b5d443569791afbb4fbcf7c8b7ad01d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 6 Apr 2020 10:15:50 +0530 Subject: [PATCH 284/929] fix: update attendace from leave application (#21154) --- erpnext/hr/doctype/leave_application/leave_application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 39e5e1132ca..a800923a8c2 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -122,7 +122,7 @@ class LeaveApplication(Document): if self.status == "Approved": for dt in daterange(getdate(self.from_date), getdate(self.to_date)): date = dt.strftime("%Y-%m-%d") - status = "Half Day" if date == self.half_day_date else "On Leave" + status = "Half Day" if getdate(date) == getdate(self.half_day_date) else "On Leave" attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee, attendance_date = date, docstatus = ('!=', 2))) From 4ea7df69b49d82a74589c8ee2b4b267506781dd1 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 6 Apr 2020 12:34:38 +0530 Subject: [PATCH 285/929] fix: change request status on payment entry cancel --- .../doctype/payment_entry/payment_entry.py | 5 ++++ .../payment_request/payment_request.py | 27 +++++++++++++------ .../payment_request/test_payment_request.py | 17 ++++++++++++ erpnext/hooks.py | 2 +- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index d65e5640263..c89742cfb7f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -81,7 +81,12 @@ class PaymentEntry(AccountsController): self.update_advance_paid() self.update_expense_claim() self.delink_advance_entry_references() + self.set_payment_req_status() self.set_status() + + def set_payment_req_status(self): + from erpnext.accounts.doctype.payment_request.payment_request import update_payment_req_status + update_payment_req_status(self, None) def update_outstanding_amounts(self): self.set_missing_ref_details(force=True) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 9c74de4af61..b51fc355764 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -417,20 +417,31 @@ def make_payment_entry(docname): doc = frappe.get_doc("Payment Request", docname) return doc.create_payment_entry(submit=False).as_dict() -def make_status_as_paid(doc, method): +def update_payment_req_status(doc, method): + from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details + for ref in doc.references: payment_request_name = frappe.db.get_value("Payment Request", {"reference_doctype": ref.reference_doctype, "reference_name": ref.reference_name, "docstatus": 1}) if payment_request_name: - doc = frappe.get_doc("Payment Request", payment_request_name) - if doc.status != "Paid" and ref.outstanding_amount <= ref.allocated_amount: - doc.db_set('status', 'Paid') - frappe.db.commit() - elif doc.status != "Partially Paid" and ref.outstanding_amount != ref.allocated_amount: - doc.db_set('status', 'Partially Paid') - frappe.db.commit() + ref_details = get_reference_details(ref.reference_doctype, ref.reference_name, doc.party_account_currency) + pay_req_doc = frappe.get_doc('Payment Request', payment_request_name) + status = pay_req_doc.status + + if status != "Paid" and not ref_details.outstanding_amount: + status = 'Paid' + elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount: + status = 'Partially Paid' + elif ref_details.outstanding_amount == ref_details.total_amount: + if pay_req_doc.payment_request_type == 'Outward': + status = 'Initiated' + elif pay_req_doc.payment_request_type == 'Inward': + status = 'Requested' + + pay_req_doc.db_set('status', status) + frappe.db.commit() def get_dummy_message(doc): return frappe.render_template("""{% if doc.contact_person -%} diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 188ab0aecf3..8a10e2cbd95 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -101,6 +101,23 @@ class TestPaymentRequest(unittest.TestCase): self.assertEqual(expected_gle[gle.account][2], gle.credit) self.assertEqual(expected_gle[gle.account][3], gle.against_voucher) + def test_status(self): + si_usd = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", + currency="USD", conversion_rate=50) + + pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com", + mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1) + + pe = pr.create_payment_entry() + pr.load_from_db() + + self.assertEqual(pr.status, 'Paid') + + pe.cancel() + pr.load_from_db() + + self.assertEqual(pr.status, 'Requested') + def test_multiple_payment_entries_against_sales_order(self): # Make Sales Order, grand_total = 1000 so = make_sales_order() diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 7f268d6ba14..60f4b50c515 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -245,7 +245,7 @@ doc_events = { "on_trash": "erpnext.regional.check_deletion_permission" }, "Payment Entry": { - "on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.make_status_as_paid"], + "on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status"], "on_trash": "erpnext.regional.check_deletion_permission" }, 'Address': { From de9c73c5cd89b8e071388a3b299aa81787472d4e Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 2 Apr 2020 22:17:41 +0530 Subject: [PATCH 286/929] fix: tests --- .../assets/doctype/asset_category/asset_category.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py index 85e5d98d163..770b1ee9a41 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -23,19 +23,20 @@ class AssetCategory(Document): account_type_map = { 'fixed_asset_account': { 'account_type': 'Fixed Asset' }, 'accumulated_depreciation_account': { 'account_type': 'Accumulated Depreciation' }, - 'depreciation_expense_account': { 'account_type': 'Expense' }, + 'depreciation_expense_account': { 'root_type': 'Expense' }, 'capital_work_in_progress_account': { 'account_type': 'Capital Work in Progress' } } for d in self.accounts: for fieldname in account_type_map.keys(): if d.get(fieldname): selected_account = d.get(fieldname) - selected_account_type = frappe.db.get_value('Account', selected_account, 'account_type') - expected_account_type = account_type_map[fieldname]['account_type'] + key_to_match = account_type_map[fieldname].keys()[0] + selected_key_type = frappe.db.get_value('Account', selected_account, key_to_match) + expected_key_type = account_type_map[fieldname][key_to_match] - if selected_account_type != expected_account_type: - frappe.throw(_("Row #{}: Account Type of {} should be {}. Please modify the account type or select a different account." - .format(d.idx, frappe.bold(selected_account), frappe.bold(expected_account_type))), + if selected_key_type != expected_key_type: + frappe.throw(_("Row #{}: {} of {} should be {}. Please modify the account or select a different account." + .format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_type))), title=_("Invalid Account")) From d7389b1920f29b8720c2b065b12f1901dddde87f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 6 Apr 2020 17:46:34 +0530 Subject: [PATCH 287/929] fix: material request type manufacture shows items with Is Purchase Item enabled --- erpnext/public/js/controllers/buying.js | 6 ----- .../material_request/material_request.js | 23 +++++++++++++++---- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 3d4c4a64595..27a9de95e0e 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -85,12 +85,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ filters:{ 'is_sub_contracted_item': 1 } } } - else if (me.frm.doc.material_request_type == "Customer Provided") { - return{ - query: "erpnext.controllers.queries.item_query", - filters:{ 'customer': me.frm.doc.customer } - } - } else { return{ query: "erpnext.controllers.queries.item_query", diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 7ef2d6460f6..6110ea822cf 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -19,11 +19,6 @@ frappe.ui.form.on('Material Request', { frm.set_indicator_formatter('item_code', function(doc) { return (doc.qty<=doc.ordered_qty) ? "green" : "orange"; }); - frm.set_query("item_code", "items", function() { - return { - query: "erpnext.controllers.queries.item_query" - }; - }); }, onload: function(frm) { @@ -145,6 +140,8 @@ frappe.ui.form.on('Material Request', { }, get_item_data: function(frm, item) { + if (item && !item.item_code) { return; } + frm.call({ method: "erpnext.stock.get_item_details.get_item_details", child: item, @@ -359,6 +356,22 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten set_schedule_date(this.frm); }, + onload: function(doc, cdt, cdn) { + this.frm.set_query("item_code", "items", function() { + if (doc.material_request_type == "Customer Provided") { + return{ + query: "erpnext.controllers.queries.item_query", + filters:{ 'customer': me.frm.doc.customer } + } + } else if (doc.material_request_type != "Manufacture") { + return{ + query: "erpnext.controllers.queries.item_query", + filters: {'is_purchase_item': 1} + } + } + }); + }, + items_add: function(doc, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); if(doc.schedule_date) { From 34288b0f38017b0ce9e5408f77e4b97afd229f25 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 6 Apr 2020 18:25:23 +0530 Subject: [PATCH 288/929] feat: (minor) purchase register filters --- .../purchase_register/purchase_register.js | 27 +++++++++++++++++ .../purchase_register/purchase_register.py | 29 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js index 42b35c2a997..b2b95b2b81b 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.js +++ b/erpnext/accounts/report/purchase_register/purchase_register.js @@ -34,6 +34,33 @@ frappe.query_reports["Purchase Register"] = { "label": __("Mode of Payment"), "fieldtype": "Link", "options": "Mode of Payment" + }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center" + }, + { + "fieldname":"warehouse", + "label": __("Warehouse"), + "fieldtype": "Link", + "options": "Warehouse" + }, + { + "fieldname":"item_group", + "label": __("Item Group"), + "fieldtype": "Link", + "options": "Item Group" } ] } + +erpnext.dimension_filters.forEach((dimension) => { + frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); +}); \ No newline at end of file diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 3f8abb76e26..795dcb18811 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_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, get_dimension_with_children def execute(filters=None): return _execute(filters) @@ -134,6 +135,34 @@ def get_conditions(filters): if filters.get("mode_of_payment"): conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s" + if filters.get("cost_center"): + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.cost_center, '') = %(cost_center)s)""" + + if filters.get("warehouse"): + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.warehouse, '') = %(warehouse)s)""" + + if filters.get("item_group"): + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.item_group, '') = %(item_group)s)""" + + accounting_dimensions = get_accounting_dimensions(as_list=False) + + if accounting_dimensions: + for dimension in accounting_dimensions: + if filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + filters.get(dimension.fieldname)) + + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.{0}, '') in %({0})s)""".format(dimension.fieldname) + return conditions def get_invoices(filters, additional_query_columns): From 2dd488f878d008e86a467fa635ae4db7a8a4a5b4 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 6 Apr 2020 22:13:13 +0530 Subject: [PATCH 289/929] feat: Scan Barcode in Purchase Receipt --- erpnext/public/js/controllers/transaction.js | 2 ++ .../stock/doctype/purchase_receipt/purchase_receipt.json | 8 +++++++- erpnext/stock/get_item_details.py | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index e1c38a91297..cfdcbb816a7 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -473,6 +473,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ item_code: item.item_code, barcode: item.barcode, serial_no: item.serial_no, + batch_no: item.batch_no, set_warehouse: me.frm.doc.set_warehouse, warehouse: item.warehouse, customer: me.frm.doc.customer || me.frm.doc.party_name, @@ -599,6 +600,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ // Add the new list to the serial no. field in grid with each in new line item.serial_no = valid_serial_nos.join('\n'); + item.conversion_factor = item.conversion_factor || 1; refresh_field("serial_no", item.name, item.parentfield); if(!doc.is_return && cint(user_defaults.set_qty_in_transactions_based_on_serial_no_input)) { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 63ef7ca8d82..00a50a88ef0 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -47,6 +47,7 @@ "is_subcontracted", "supplier_warehouse", "items_section", + "scan_barcode", "items", "pricing_rule_details", "pricing_rules", @@ -1053,13 +1054,18 @@ "oldfieldtype": "Date", "print_width": "100px", "width": "100px" + }, + { + "fieldname": "scan_barcode", + "fieldtype": "Data", + "label": "Scan Barcode" } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2019-12-30 19:12:49.709711", + "modified": "2020-04-06 16:31:37.444891", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index d15a2597386..54dc685e4ef 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -244,7 +244,7 @@ def get_basic_details(args, item, overwrite_warehouse=True): 'item_group_defaults': item_group_defaults, 'brand_defaults': brand_defaults }) - + warehouse = get_item_warehouse(item, args, overwrite_warehouse, defaults) if args.get('doctype') == "Material Request" and not args.get('material_request_type'): @@ -278,7 +278,7 @@ def get_basic_details(args, item, overwrite_warehouse=True): "cost_center": get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults), 'has_serial_no': item.has_serial_no, 'has_batch_no': item.has_batch_no, - "batch_no": None, + "batch_no": args.get("batch_no"), "uom": args.uom, "min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "", "qty": flt(args.qty) or 1.0, @@ -375,7 +375,7 @@ def get_item_warehouse(item, args, overwrite_warehouse, defaults={}): else: warehouse = args.get('warehouse') - + return warehouse def update_barcode_value(out): From 044abbace8078c5cbb58280d4b0cab5e6e795b82 Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Tue, 7 Apr 2020 12:05:18 +0530 Subject: [PATCH 290/929] fix: Make Sales Invoice paid when it create from shopping cart (#20878) Co-authored-by: Saqib --- erpnext/accounts/doctype/payment_request/payment_request.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index bcf7159f843..f2b0d3db7a1 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -90,6 +90,7 @@ class PaymentRequest(Document): if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart"): from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice si = make_sales_invoice(self.reference_name, ignore_permissions=True) + si.allocate_advances_automatically = True si = si.insert(ignore_permissions=True) si.submit() From d2da8bd6e2effc87e86c88b5f5ca2c5704dfc6fe Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Tue, 7 Apr 2020 12:19:40 +0530 Subject: [PATCH 291/929] fix(shopping cart): UX Improvements (#21035) * fix: nontype error for resolved and moved place order button at bottom left * fix: removed inline style * fix: Request for quotation move to lower right * fix: move buttons Co-authored-by: Naren Co-authored-by: Saqib --- erpnext/public/scss/website.scss | 4 ++++ .../templates/includes/order/order_taxes.html | 20 ++++++++-------- erpnext/templates/pages/cart.html | 24 +++++++++++-------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/erpnext/public/scss/website.scss b/erpnext/public/scss/website.scss index 7b9a70d232e..735b417da17 100644 --- a/erpnext/public/scss/website.scss +++ b/erpnext/public/scss/website.scss @@ -78,3 +78,7 @@ z-index: 0; } } + +.place-order-container { + text-align: right; +} \ No newline at end of file diff --git a/erpnext/templates/includes/order/order_taxes.html b/erpnext/templates/includes/order/order_taxes.html index 4a32aa47446..ebec838d03e 100644 --- a/erpnext/templates/includes/order/order_taxes.html +++ b/erpnext/templates/includes/order/order_taxes.html @@ -10,16 +10,16 @@ {% endif %} {% for d in doc.taxes %} -{% if d.base_tax_amount > 0 %} - - - - -{% endif %} + {% if d.base_tax_amount %} + + + + + {% endif %} {% endfor %} {% if doc.doctype == 'Quotation' %} diff --git a/erpnext/templates/pages/cart.html b/erpnext/templates/pages/cart.html index 912702e386d..3033d1587d6 100644 --- a/erpnext/templates/pages/cart.html +++ b/erpnext/templates/pages/cart.html @@ -12,16 +12,6 @@ {% block header_actions %} -{% if doc.items and cart_settings.enable_checkout %} - -{% endif %} -{% if doc.items and not cart_settings.enable_checkout %} - -{% endif %} {% endblock %} {% block page_content %} @@ -55,6 +45,20 @@

{{ _('Your cart is Empty') }}

{% endif %} + {% if doc.items %} +
+ {% if cart_settings.enable_checkout %} + + {% else %} + + {% endif %} +
+ {% endif %} + {% if doc.items %} {% if doc.tc_name %} {% for tax_account in tax_accounts %} {% set tax_details = taxes.get(tax_account) %} @@ -25,7 +29,11 @@ {% if tax_details.tax_rate or not tax_details.tax_amount %} ({{ tax_details.tax_rate }}%) {% endif %} - {{ frappe.utils.fmt_money(tax_details.tax_amount / conversion_rate, None, currency) }} + {% if doc.get('is_return') %} + {{ frappe.utils.fmt_money((tax_details.tax_amount / doc.conversion_rate)|abs, None, doc.currency) }} + {% else %} + {{ frappe.utils.fmt_money(tax_details.tax_amount / doc.conversion_rate, None, doc.currency) }} + {% endif %} {% else %} From 424dbef1398cc28f3fd7534cb7f92865edec8411 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 24 Apr 2020 16:40:03 +0530 Subject: [PATCH 386/929] fix: rate gets overwritten when pricing rule is set --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 1 + erpnext/public/js/controllers/transaction.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index fc0d8b16f71..086f42b293f 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -241,6 +241,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other: item_details.update({ 'apply_rule_on_other_items': json.dumps(pricing_rule.apply_rule_on_other_items), + 'price_or_product_discount': pricing_rule.price_or_product_discount, 'apply_rule_on': (frappe.scrub(pricing_rule.apply_rule_on_other) if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on'))) }) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 85d9799ce56..6def5cb51f3 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1374,7 +1374,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ me.frm.doc.items.forEach(d => { if (in_list(data.apply_rule_on_other_items, d[data.apply_rule_on])) { for(var k in data) { - if (in_list(fields, k) && data[k]) { + if (in_list(fields, k) && data[k] && (data.price_or_product_discount === 'price' || k === 'pricing_rules')) { frappe.model.set_value(d.doctype, d.name, k, data[k]); } } From be97087d32b0f4400b825fab4920da7a1697ebc2 Mon Sep 17 00:00:00 2001 From: Diksha Jadhav Date: Fri, 24 Apr 2020 21:09:59 +0530 Subject: [PATCH 387/929] feat(accounting): show actual qty for warehouse in sales invoice --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 7a7d25aadcc..d8ba1794039 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -32,6 +32,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte me.frm.script_manager.trigger("is_pos"); me.frm.refresh_fields(); } + erpnext.queries.setup_warehouse_query(this.frm); }, refresh: function(doc, dt, dn) { From 7fedff260db7c25c1494da921e58115be4f6d9e8 Mon Sep 17 00:00:00 2001 From: Saqib Date: Sun, 26 Apr 2020 09:41:02 +0530 Subject: [PATCH 388/929] fix: (ux) set jv voucher type depending on mode of payment (#21412) --- .../hr/doctype/employee_advance/employee_advance.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index f10e3b6ce26..f0663aefa83 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -136,9 +136,18 @@ def make_bank_entry(dt, dn): def make_return_entry(employee, company, employee_advance_name, return_amount, advance_account, mode_of_payment=None): return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment) + + mode_of_payment_type = '' + if mode_of_payment: + mode_of_payment_type = frappe.get_cached_value('Mode of Payment', mode_of_payment, 'type') + if mode_of_payment_type not in ["Cash", "Bank"]: + # if mode of payment is General then it unset the type + mode_of_payment_type = None + je = frappe.new_doc('Journal Entry') je.posting_date = nowdate() - je.voucher_type = 'Bank Entry' + # if mode of payment is Bank then voucher type is Bank Entry + je.voucher_type = '{} Entry'.format(mode_of_payment_type) if mode_of_payment_type else 'Cash Entry' je.company = company je.remark = 'Return against Employee Advance: ' + employee_advance_name From 1d5ea4feee336ed24e9cf0eb6b0a48de99102652 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sun, 26 Apr 2020 12:37:52 +0530 Subject: [PATCH 389/929] feat: Income tax slab (#21406) * Feat: Multiple tax as per new taxation rule * patch:for multiple tax slab, fix: payroll and exemption validation * Test: Fixture * feat: income tax slab with other charges and tax exempted deduction components * fix: added missing init file * fix: Patch fixed * fix: Patch fixed * fix: test fixes * fix: validate duplicate exemption declaration * fix: payment entry test case Co-authored-by: Anurag Mishra --- .../payment_entry/test_payment_entry.py | 7 + .../doctype/sales_invoice/sales_invoice.js | 10 +- .../doctype/employee_other_income/__init__.py | 0 .../employee_other_income.js | 8 + .../employee_other_income.json | 138 +++ .../employee_other_income.py | 10 + .../test_employee_other_income.py | 10 + .../employee_tax_exemption_declaration.json | 746 +++----------- .../employee_tax_exemption_declaration.py | 20 +- ...test_employee_tax_exemption_declaration.py | 2 +- ...ployee_tax_exemption_proof_submission.json | 731 +++----------- ...employee_tax_exemption_proof_submission.py | 6 +- .../hr/doctype/income_tax_slab/__init__.py | 0 .../income_tax_slab/income_tax_slab.js | 6 + .../income_tax_slab/income_tax_slab.json | 160 +++ .../income_tax_slab/income_tax_slab.py | 10 + .../income_tax_slab/test_income_tax_slab.py | 10 + .../income_tax_slab_other_charges/__init__.py | 0 .../income_tax_slab_other_charges.json | 75 ++ .../income_tax_slab_other_charges.py | 10 + .../payroll_period/payroll_period.json | 467 ++------- .../doctype/payroll_period/payroll_period.py | 5 +- .../salary_component/salary_component.json | 525 +++++----- .../salary_component/test_records.json | 4 - .../salary_component/test_salary_component.py | 1 - .../doctype/salary_detail/salary_detail.json | 931 ++++-------------- erpnext/hr/doctype/salary_slip/salary_slip.py | 142 ++- .../doctype/salary_slip/test_salary_slip.py | 76 +- .../salary_structure/salary_structure.js | 1 + .../salary_structure/salary_structure.py | 23 +- .../salary_structure/test_salary_structure.py | 20 +- .../salary_structure_assignment.js | 10 + .../salary_structure_assignment.json | 13 +- erpnext/hr/utils.py | 13 + erpnext/patches.txt | 1 + .../v11_0/set_salary_component_properties.py | 3 +- erpnext/patches/v13_0/__init__.py | 0 ..._from_payroll_period_to_income_tax_slab.py | 99 ++ erpnext/regional/india/setup.py | 18 +- 39 files changed, 1598 insertions(+), 2713 deletions(-) create mode 100644 erpnext/hr/doctype/employee_other_income/__init__.py create mode 100644 erpnext/hr/doctype/employee_other_income/employee_other_income.js create mode 100644 erpnext/hr/doctype/employee_other_income/employee_other_income.json create mode 100644 erpnext/hr/doctype/employee_other_income/employee_other_income.py create mode 100644 erpnext/hr/doctype/employee_other_income/test_employee_other_income.py create mode 100644 erpnext/hr/doctype/income_tax_slab/__init__.py create mode 100644 erpnext/hr/doctype/income_tax_slab/income_tax_slab.js create mode 100644 erpnext/hr/doctype/income_tax_slab/income_tax_slab.json create mode 100644 erpnext/hr/doctype/income_tax_slab/income_tax_slab.py create mode 100644 erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py create mode 100644 erpnext/hr/doctype/income_tax_slab_other_charges/__init__.py create mode 100644 erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json create mode 100644 erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py create mode 100644 erpnext/patches/v13_0/__init__.py create mode 100644 erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 80a11b1bdff..756cc8ec547 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -199,6 +199,13 @@ class TestPaymentEntry(unittest.TestCase): pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC", currency="USD", conversion_rate=50) + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC") + pe.reference_no = "1" + pe.reference_date = "2016-01-01" + pe.source_exchange_rate = 50 + pe.insert() + pe.submit() + outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"]) self.assertEqual(flt(outstanding_amount), 0) self.assertEqual(status, 'Paid') diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index d8ba1794039..18a791d38ce 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -587,7 +587,9 @@ frappe.ui.form.on('Sales Invoice', { frm.set_query("account_for_change_amount", function() { return { filters: { - account_type: ['in', ["Cash", "Bank"]] + account_type: ['in', ["Cash", "Bank"]], + company: frm.doc.company, + is_group: 0 } }; }); @@ -668,7 +670,8 @@ frappe.ui.form.on('Sales Invoice', { frm.fields_dict["loyalty_redemption_account"].get_query = function() { return { filters:{ - "company": frm.doc.company + "company": frm.doc.company, + "is_group": 0 } } }; @@ -677,7 +680,8 @@ frappe.ui.form.on('Sales Invoice', { frm.fields_dict["loyalty_redemption_cost_center"].get_query = function() { return { filters:{ - "company": frm.doc.company + "company": frm.doc.company, + "is_group": 0 } } }; diff --git a/erpnext/hr/doctype/employee_other_income/__init__.py b/erpnext/hr/doctype/employee_other_income/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.js b/erpnext/hr/doctype/employee_other_income/employee_other_income.js new file mode 100644 index 00000000000..c1a74e863ba --- /dev/null +++ b/erpnext/hr/doctype/employee_other_income/employee_other_income.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Other Income', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.json b/erpnext/hr/doctype/employee_other_income/employee_other_income.json new file mode 100644 index 00000000000..2dd6c10988d --- /dev/null +++ b/erpnext/hr/doctype/employee_other_income/employee_other_income.json @@ -0,0 +1,138 @@ +{ + "actions": [], + "autoname": "HR-INCOME-.######", + "creation": "2020-03-18 15:04:40.767434", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "payroll_period", + "column_break_3", + "company", + "source", + "amount", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fieldname": "payroll_period", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Payroll Period", + "options": "Payroll Period", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "source", + "fieldtype": "Data", + "label": "Source" + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "Company:company:default_currency", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Other Income", + "print_hide": 1, + "read_only": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-03-19 18:06:45.361830", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Other Income", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.py b/erpnext/hr/doctype/employee_other_income/employee_other_income.py new file mode 100644 index 00000000000..ab63c0de623 --- /dev/null +++ b/erpnext/hr/doctype/employee_other_income/employee_other_income.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class EmployeeOtherIncome(Document): + pass diff --git a/erpnext/hr/doctype/employee_other_income/test_employee_other_income.py b/erpnext/hr/doctype/employee_other_income/test_employee_other_income.py new file mode 100644 index 00000000000..2eeca7a23de --- /dev/null +++ b/erpnext/hr/doctype/employee_other_income/test_employee_other_income.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestEmployeeOtherIncome(unittest.TestCase): + pass diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json index e102ff8d705..18fad85c4b3 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json @@ -1,620 +1,180 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-TAX-DEC-.YYYY.-.#####", - "beta": 0, - "creation": "2018-04-13 16:53:36.175504", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-TAX-DEC-.YYYY.-.#####", + "creation": "2018-04-13 16:53:36.175504", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "column_break_2", + "payroll_period", + "company", + "amended_from", + "section_break_8", + "declarations", + "section_break_10", + "total_declared_amount", + "column_break_12", + "total_exemption_amount" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "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 - }, + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fetch_if_empty": 0, - "fieldname": "employee_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": "Employee 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 - }, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fetch_if_empty": 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": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "payroll_period", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Payroll Period", - "length": 0, - "no_copy": 0, - "options": "Payroll Period", - "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 - }, + "fieldname": "payroll_period", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Payroll Period", + "options": "Payroll Period", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.company", - "fetch_if_empty": 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, - "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 - }, + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Employee Tax Exemption Declaration", - "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", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Tax Exemption Declaration", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "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_8", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "declarations", - "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": "Declarations", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Declaration Category", - "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": "declarations", + "fieldtype": "Table", + "label": "Declarations", + "options": "Employee Tax Exemption Declaration Category" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_10", - "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_10", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "total_declared_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 Declared Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_declared_amount", + "fieldtype": "Currency", + "label": "Total Declared Amount", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_12", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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_12", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "total_exemption_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 Exemption Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "other_incomes_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": "Other Incomes", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "income_from_other_sources", - "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": "Income From Other Sources", - "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": "total_exemption_amount", + "fieldtype": "Currency", + "label": "Total Exemption Amount", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-05-11 16:13:50.472670", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Declaration", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-03-18 14:56:25.625717", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Tax Exemption Declaration", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py index f2bba7afed7..fb71a2877a1 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py @@ -8,31 +8,17 @@ from frappe.model.document import Document from frappe import _ from frappe.utils import flt from frappe.model.mapper import get_mapped_doc -from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, calculate_annual_eligible_hra_exemption - -class DuplicateDeclarationError(frappe.ValidationError): pass +from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \ + calculate_annual_eligible_hra_exemption, validate_duplicate_exemption_for_payroll_period class EmployeeTaxExemptionDeclaration(Document): def validate(self): validate_tax_declaration(self.declarations) - self.validate_duplicate() + validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee) self.set_total_declared_amount() self.set_total_exemption_amount() self.calculate_hra_exemption() - def validate_duplicate(self): - duplicate = frappe.db.get_value("Employee Tax Exemption Declaration", - filters = { - "employee": self.employee, - "payroll_period": self.payroll_period, - "name": ["!=", self.name], - "docstatus": ["!=", 2] - } - ) - if duplicate: - frappe.throw(_("Duplicate Tax Declaration of {0} for period {1}") - .format(self.employee, self.payroll_period), DuplicateDeclarationError) - def set_total_declared_amount(self): self.total_declared_amount = 0.0 for d in self.declarations: diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py index 9c87bbd1f30..9549fd1b757 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe, erpnext import unittest from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration import DuplicateDeclarationError +from erpnext.hr.utils import DuplicateDeclarationError class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): def setUp(self): diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json index 9792bd1db61..e13b1ac8888 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json @@ -1,635 +1,140 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-TAX-PRF-.YYYY.-.#####", - "beta": 0, - "creation": "2018-04-13 17:24:11.456132", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-TAX-PRF-.YYYY.-.#####", + "creation": "2018-04-13 17:24:11.456132", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "column_break_2", + "submission_date", + "payroll_period", + "company", + "section_break_5", + "tax_exemption_proofs", + "section_break_10", + "total_actual_amount", + "column_break_12", + "exemption_amount", + "attachment_section", + "attachments", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "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 - }, + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fetch_if_empty": 0, - "fieldname": "employee_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": "Employee 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 - }, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fetch_if_empty": 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": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fetch_if_empty": 0, - "fieldname": "submission_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": "Submission Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "submission_date", + "fieldtype": "Date", + "label": "Submission Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "payroll_period", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Payroll Period", - "length": 0, - "no_copy": 0, - "options": "Payroll Period", - "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 - }, + "fieldname": "payroll_period", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Payroll Period", + "options": "Payroll Period", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.company", - "fetch_if_empty": 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, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "tax_exemption_proofs", - "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": "Tax Exemption Proofs", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Proof Submission Detail", - "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": "tax_exemption_proofs", + "fieldtype": "Table", + "label": "Tax Exemption Proofs", + "options": "Employee Tax Exemption Proof Submission Detail" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_10", - "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_10", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "total_actual_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 Actual Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_actual_amount", + "fieldtype": "Currency", + "label": "Total Actual Amount", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_12", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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_12", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "exemption_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 Exemption Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "exemption_amount", + "fieldtype": "Currency", + "label": "Total Exemption Amount", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "other_incomes_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": "Other Incomes", - "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": "attachment_section", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "income_from_other_sources", - "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": "Income From Other Sources", - "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": "attachments", + "fieldtype": "Attach", + "label": "Attachments" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "attachment_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "attachments", - "fieldtype": "Attach", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Attachments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Employee Tax Exemption Proof Submission", - "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", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Tax Exemption Proof Submission", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-05-13 12:17:18.045171", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Proof Submission", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-03-18 14:55:51.420016", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Tax Exemption Proof Submission", + "owner": "Administrator", "permissions": [ { "amend": 1, diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py index 97ceb63476b..5bc33a65f2c 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py @@ -7,7 +7,8 @@ import frappe from frappe.model.document import Document from frappe import _ from frappe.utils import flt -from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, calculate_hra_exemption_for_period +from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \ + calculate_hra_exemption_for_period, validate_duplicate_exemption_for_payroll_period class EmployeeTaxExemptionProofSubmission(Document): def validate(self): @@ -15,6 +16,7 @@ class EmployeeTaxExemptionProofSubmission(Document): self.set_total_actual_amount() self.set_total_exemption_amount() self.calculate_hra_exemption() + validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee) def set_total_actual_amount(self): self.total_actual_amount = flt(self.get("house_rent_payment_amount")) @@ -32,4 +34,4 @@ class EmployeeTaxExemptionProofSubmission(Document): self.exemption_amount += hra_exemption["total_eligible_hra_exemption"] self.monthly_hra_exemption = hra_exemption["monthly_exemption"] self.monthly_house_rent = hra_exemption["monthly_house_rent"] - self.total_eligible_hra_exemption = hra_exemption["total_eligible_hra_exemption"] \ No newline at end of file + self.total_eligible_hra_exemption = hra_exemption["total_eligible_hra_exemption"] diff --git a/erpnext/hr/doctype/income_tax_slab/__init__.py b/erpnext/hr/doctype/income_tax_slab/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.js b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.js new file mode 100644 index 00000000000..73a54eb8dd9 --- /dev/null +++ b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.js @@ -0,0 +1,6 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Income Tax Slab', { + +}); diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json new file mode 100644 index 00000000000..6d89b197d27 --- /dev/null +++ b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json @@ -0,0 +1,160 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2020-03-17 16:50:35.564915", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "effective_from", + "company", + "column_break_3", + "allow_tax_exemption", + "standard_tax_exemption_amount", + "disabled", + "amended_from", + "taxable_salary_slabs_section", + "slabs", + "taxes_and_charges_on_income_tax_section", + "other_taxes_and_charges" + ], + "fields": [ + { + "fieldname": "effective_from", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Effective from", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "If enabled, Tax Exemption Declaration will be considered for income tax calculation.", + "fieldname": "allow_tax_exemption", + "fieldtype": "Check", + "label": "Allow Tax Exemption" + }, + { + "fieldname": "taxable_salary_slabs_section", + "fieldtype": "Section Break", + "label": "Taxable Salary Slabs" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Income Tax Slab", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "slabs", + "fieldtype": "Table", + "label": "Taxable Salary Slabs", + "options": "Taxable Salary Slab", + "reqd": 1 + }, + { + "allow_on_submit": 1, + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "depends_on": "allow_tax_exemption", + "fieldname": "standard_tax_exemption_amount", + "fieldtype": "Currency", + "label": "Standard Tax Exemption Amount", + "options": "Company:company:default_currency" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "collapsible": 1, + "fieldname": "taxes_and_charges_on_income_tax_section", + "fieldtype": "Section Break", + "label": "Taxes and Charges on Income Tax" + }, + { + "fieldname": "other_taxes_and_charges", + "fieldtype": "Table", + "label": "Other Taxes and Charges", + "options": "Income Tax Slab Other Charges" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-04-24 12:28:36.805904", + "modified_by": "Administrator", + "module": "HR", + "name": "Income Tax Slab", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.py b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.py new file mode 100644 index 00000000000..253f023f68b --- /dev/null +++ b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class IncomeTaxSlab(Document): + pass diff --git a/erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py b/erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py new file mode 100644 index 00000000000..deaaf650a96 --- /dev/null +++ b/erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestIncomeTaxSlab(unittest.TestCase): + pass diff --git a/erpnext/hr/doctype/income_tax_slab_other_charges/__init__.py b/erpnext/hr/doctype/income_tax_slab_other_charges/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json b/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json new file mode 100644 index 00000000000..b23fb3dc317 --- /dev/null +++ b/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json @@ -0,0 +1,75 @@ +{ + "actions": [], + "creation": "2020-04-24 11:46:59.041180", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "description", + "column_break_2", + "percent", + "conditions_section", + "min_taxable_income", + "column_break_7", + "max_taxable_income" + ], + "fields": [ + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "columns": 2, + "fieldname": "min_taxable_income", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Min Taxable Income", + "options": "Company:company:default_currency" + }, + { + "columns": 4, + "fieldname": "description", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Description", + "reqd": 1 + }, + { + "columns": 2, + "fieldname": "percent", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "Percent", + "reqd": 1 + }, + { + "fieldname": "conditions_section", + "fieldtype": "Section Break", + "label": "Conditions" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "columns": 2, + "fieldname": "max_taxable_income", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Max Taxable Income", + "options": "Company:company:default_currency" + } + ], + "istable": 1, + "links": [], + "modified": "2020-04-24 13:27:43.598967", + "modified_by": "Administrator", + "module": "HR", + "name": "Income Tax Slab Other Charges", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py b/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py new file mode 100644 index 00000000000..b4098ecbf3e --- /dev/null +++ b/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class IncomeTaxSlabOtherCharges(Document): + pass diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.json b/erpnext/hr/doctype/payroll_period/payroll_period.json index c9bac095f9f..c0fa506e7f0 100644 --- a/erpnext/hr/doctype/payroll_period/payroll_period.json +++ b/erpnext/hr/doctype/payroll_period/payroll_period.json @@ -1,401 +1,102 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "Prompt", - "beta": 0, - "creation": "2018-04-13 15:18:53.698553", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "autoname": "Prompt", + "creation": "2018-04-13 15:18:53.698553", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "column_break_2", + "start_date", + "end_date", + "section_break_5", + "periods" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "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 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "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": 0, - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "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": 0, - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "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": "Payroll Periods", - "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_5", + "fieldtype": "Section Break", + "hidden": 1, + "label": "Payroll Periods" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "periods", - "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": "Payroll Periods", - "length": 0, - "no_copy": 0, - "options": "Payroll Period Date", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_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, - "label": "Taxable Salary Slabs", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "taxable_salary_slabs", - "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": "Taxable Salary Slabs", - "length": 0, - "no_copy": 0, - "options": "Taxable Salary Slab", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "standard_tax_exemption_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": "Standard Tax Exemption Amount", - "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": "periods", + "fieldtype": "Table", + "label": "Payroll Periods", + "options": "Payroll Period Date" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-26 01:45:03.160929", - "modified_by": "Administrator", - "module": "HR", - "name": "Payroll Period", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-03-18 18:13:23.859980", + "modified_by": "Administrator", + "module": "HR", + "name": "Payroll Period", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "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": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, "write": 1 - }, + }, { - "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": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.py b/erpnext/hr/doctype/payroll_period/payroll_period.py index c1769591eaf..6956c382854 100644 --- a/erpnext/hr/doctype/payroll_period/payroll_period.py +++ b/erpnext/hr/doctype/payroll_period/payroll_period.py @@ -45,8 +45,9 @@ class PayrollPeriod(Document): + _(") for {0}").format(self.company) frappe.throw(msg) -def get_payroll_period_days(start_date, end_date, employee): - company = frappe.db.get_value("Employee", employee, "company") +def get_payroll_period_days(start_date, end_date, employee, company=None): + if not company: + company = frappe.db.get_value("Employee", employee, "company") payroll_period = frappe.db.sql(""" select name, start_date, end_date from `tabPayroll Period` diff --git a/erpnext/hr/doctype/salary_component/salary_component.json b/erpnext/hr/doctype/salary_component/salary_component.json index 986030d8c58..5487e1dee85 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.json +++ b/erpnext/hr/doctype/salary_component/salary_component.json @@ -1,264 +1,263 @@ { - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:salary_component", - "creation": "2016-06-30 15:42:43.631931", - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "salary_component", - "salary_component_abbr", - "type", - "description", - "column_break_4", - "is_payable", - "depends_on_payment_days", - "is_tax_applicable", - "deduct_full_tax_on_selected_payroll_date", - "round_to_the_nearest_integer", - "statistical_component", - "do_not_include_in_total", - "disabled", - "flexible_benefits", - "is_flexible_benefit", - "max_benefit_amount", - "column_break_9", - "pay_against_benefit_claim", - "only_tax_impact", - "create_separate_payment_entry_against_benefit_claim", - "section_break_11", - "variable_based_on_taxable_salary", - "section_break_5", - "accounts", - "condition_and_formula", - "condition", - "amount", - "amount_based_on_formula", - "formula", - "column_break_28", - "help" - ], - "fields": [ - { - "fieldname": "salary_component", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Name", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "salary_component_abbr", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Abbr", - "print_width": "120px", - "reqd": 1, - "width": "120px" - }, - { - "fieldname": "type", - "fieldtype": "Select", - "in_standard_filter": 1, - "label": "Type", - "options": "Earning\nDeduction", - "reqd": 1 - }, - { - "default": "1", - "depends_on": "eval:doc.type == \"Earning\"", - "fieldname": "is_tax_applicable", - "fieldtype": "Check", - "label": "Is Tax Applicable" - }, - { - "default": "1", - "fieldname": "is_payable", - "fieldtype": "Check", - "label": "Is Payable" - }, - { - "default": "1", - "fieldname": "depends_on_payment_days", - "fieldtype": "Check", - "label": "Depends on Payment Days", - "print_hide": 1 - }, - { - "default": "0", - "fieldname": "do_not_include_in_total", - "fieldtype": "Check", - "label": "Do Not Include in Total" - }, - { - "default": "0", - "depends_on": "is_tax_applicable", - "fieldname": "deduct_full_tax_on_selected_payroll_date", - "fieldtype": "Check", - "label": "Deduct Full Tax on Selected Payroll Date" - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "disabled", - "fieldtype": "Check", - "label": "Disabled" - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "in_list_view": 1, - "label": "Description" - }, - { - "default": "0", - "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", - "fieldname": "statistical_component", - "fieldtype": "Check", - "label": "Statistical Component" - }, - { - "depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1", - "fieldname": "flexible_benefits", - "fieldtype": "Section Break", - "label": "Flexible Benefits" - }, - { - "default": "0", - "fieldname": "is_flexible_benefit", - "fieldtype": "Check", - "label": "Is Flexible Benefit" - }, - { - "depends_on": "is_flexible_benefit", - "fieldname": "max_benefit_amount", - "fieldtype": "Currency", - "label": "Max Benefit Amount (Yearly)" - }, - { - "fieldname": "column_break_9", - "fieldtype": "Column Break" - }, - { - "default": "0", - "depends_on": "is_flexible_benefit", - "fieldname": "pay_against_benefit_claim", - "fieldtype": "Check", - "label": "Pay Against Benefit Claim" - }, - { - "default": "0", - "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.create_separate_payment_entry_against_benefit_claim !=1", - "fieldname": "only_tax_impact", - "fieldtype": "Check", - "label": "Only Tax Impact (Cannot Claim But Part of Taxable Income)" - }, - { - "default": "0", - "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.only_tax_impact !=1", - "fieldname": "create_separate_payment_entry_against_benefit_claim", - "fieldtype": "Check", - "label": "Create Separate Payment Entry Against Benefit Claim" - }, - { - "depends_on": "eval:doc.type=='Deduction'", - "fieldname": "section_break_11", - "fieldtype": "Section Break" - }, - { - "default": "0", - "fieldname": "variable_based_on_taxable_salary", - "fieldtype": "Check", - "label": "Variable Based On Taxable Salary" - }, - { - "depends_on": "eval:doc.statistical_component != 1", - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "label": "Accounts" - }, - { - "fieldname": "accounts", - "fieldtype": "Table", - "label": "Accounts", - "options": "Salary Component Account" - }, - { - "collapsible": 1, - "depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1", - "fieldname": "condition_and_formula", - "fieldtype": "Section Break", - "label": "Condition and Formula" - }, - { - "fieldname": "condition", - "fieldtype": "Code", - "label": "Condition" - }, - { - "default": "0", - "fieldname": "amount_based_on_formula", - "fieldtype": "Check", - "label": "Amount based on formula" - }, - { - "depends_on": "amount_based_on_formula", - "fieldname": "formula", - "fieldtype": "Code", - "label": "Formula" - }, - { - "depends_on": "eval:doc.amount_based_on_formula!==1", - "fieldname": "amount", - "fieldtype": "Currency", - "label": "Amount" - }, - { - "fieldname": "column_break_28", - "fieldtype": "Column Break" - }, - { - "fieldname": "help", - "fieldtype": "HTML", - "label": "Help", - "options": "

Help

\n\n

Notes:

\n\n
    \n
  1. Use field base for using base salary of the Employee
  2. \n
  3. Use Salary Component abbreviations in conditions and formulas. BS = Basic Salary
  4. \n
  5. Use field name for employee details in conditions and formulas. Employment Type = employment_typeBranch = branch
  6. \n
  7. Use field name from Salary Slip in conditions and formulas. Payment Days = payment_daysLeave without pay = leave_without_pay
  8. \n
  9. Direct Amount can also be entered based on Condtion. See example 3
\n\n

Examples

\n
    \n
  1. Calculating Basic Salary based on base\n
    Condition: base < 10000
    \n
    Formula: base * .2
  2. \n
  3. Calculating HRA based on Basic SalaryBS \n
    Condition: BS > 2000
    \n
    Formula: BS * .1
  4. \n
  5. Calculating TDS based on Employment Typeemployment_type \n
    Condition: employment_type==\"Intern\"
    \n
    Amount: 1000
  6. \n
" - }, - { - "default": "0", - "fieldname": "round_to_the_nearest_integer", - "fieldtype": "Check", - "label": "Round to the Nearest Integer" - } - ], - "icon": "fa fa-flag", - "modified": "2019-06-05 11:34:14.231228", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Component", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - }, - { - "read": 1, - "role": "Employee" - } - ], - "sort_field": "modified", - "sort_order": "DESC" - } \ No newline at end of file + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:salary_component", + "creation": "2016-06-30 15:42:43.631931", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "salary_component", + "salary_component_abbr", + "type", + "description", + "column_break_4", + "depends_on_payment_days", + "is_tax_applicable", + "deduct_full_tax_on_selected_payroll_date", + "variable_based_on_taxable_salary", + "exempted_from_income_tax", + "round_to_the_nearest_integer", + "statistical_component", + "do_not_include_in_total", + "disabled", + "flexible_benefits", + "is_flexible_benefit", + "max_benefit_amount", + "column_break_9", + "pay_against_benefit_claim", + "only_tax_impact", + "create_separate_payment_entry_against_benefit_claim", + "section_break_5", + "accounts", + "condition_and_formula", + "condition", + "amount", + "amount_based_on_formula", + "formula", + "column_break_28", + "help" + ], + "fields": [ + { + "fieldname": "salary_component", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "salary_component_abbr", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Abbr", + "print_width": "120px", + "reqd": 1, + "width": "120px" + }, + { + "fieldname": "type", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Type", + "options": "Earning\nDeduction", + "reqd": 1 + }, + { + "default": "1", + "depends_on": "eval:doc.type == \"Earning\"", + "fieldname": "is_tax_applicable", + "fieldtype": "Check", + "label": "Is Tax Applicable" + }, + { + "default": "1", + "fieldname": "depends_on_payment_days", + "fieldtype": "Check", + "label": "Depends on Payment Days", + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "do_not_include_in_total", + "fieldtype": "Check", + "label": "Do Not Include in Total" + }, + { + "default": "0", + "depends_on": "eval:doc.is_tax_applicable && doc.type=='Earning'", + "fieldname": "deduct_full_tax_on_selected_payroll_date", + "fieldtype": "Check", + "label": "Deduct Full Tax on Selected Payroll Date" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Description" + }, + { + "default": "0", + "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", + "fieldname": "statistical_component", + "fieldtype": "Check", + "label": "Statistical Component" + }, + { + "depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1", + "fieldname": "flexible_benefits", + "fieldtype": "Section Break", + "label": "Flexible Benefits" + }, + { + "default": "0", + "fieldname": "is_flexible_benefit", + "fieldtype": "Check", + "label": "Is Flexible Benefit" + }, + { + "depends_on": "is_flexible_benefit", + "fieldname": "max_benefit_amount", + "fieldtype": "Currency", + "label": "Max Benefit Amount (Yearly)" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "is_flexible_benefit", + "fieldname": "pay_against_benefit_claim", + "fieldtype": "Check", + "label": "Pay Against Benefit Claim" + }, + { + "default": "0", + "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.create_separate_payment_entry_against_benefit_claim !=1", + "fieldname": "only_tax_impact", + "fieldtype": "Check", + "label": "Only Tax Impact (Cannot Claim But Part of Taxable Income)" + }, + { + "default": "0", + "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.only_tax_impact !=1", + "fieldname": "create_separate_payment_entry_against_benefit_claim", + "fieldtype": "Check", + "label": "Create Separate Payment Entry Against Benefit Claim" + }, + { + "default": "0", + "depends_on": "eval:doc.type == \"Deduction\"", + "fieldname": "variable_based_on_taxable_salary", + "fieldtype": "Check", + "label": "Variable Based On Taxable Salary" + }, + { + "depends_on": "eval:doc.statistical_component != 1", + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "label": "Accounts" + }, + { + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounts", + "options": "Salary Component Account" + }, + { + "collapsible": 1, + "depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1", + "fieldname": "condition_and_formula", + "fieldtype": "Section Break", + "label": "Condition and Formula" + }, + { + "fieldname": "condition", + "fieldtype": "Code", + "label": "Condition" + }, + { + "default": "0", + "fieldname": "amount_based_on_formula", + "fieldtype": "Check", + "label": "Amount based on formula" + }, + { + "depends_on": "amount_based_on_formula", + "fieldname": "formula", + "fieldtype": "Code", + "label": "Formula" + }, + { + "depends_on": "eval:doc.amount_based_on_formula!==1", + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "fieldname": "help", + "fieldtype": "HTML", + "label": "Help", + "options": "

Help

\n\n

Notes:

\n\n
    \n
  1. Use field base for using base salary of the Employee
  2. \n
  3. Use Salary Component abbreviations in conditions and formulas. BS = Basic Salary
  4. \n
  5. Use field name for employee details in conditions and formulas. Employment Type = employment_typeBranch = branch
  6. \n
  7. Use field name from Salary Slip in conditions and formulas. Payment Days = payment_daysLeave without pay = leave_without_pay
  8. \n
  9. Direct Amount can also be entered based on Condtion. See example 3
\n\n

Examples

\n
    \n
  1. Calculating Basic Salary based on base\n
    Condition: base < 10000
    \n
    Formula: base * .2
  2. \n
  3. Calculating HRA based on Basic SalaryBS \n
    Condition: BS > 2000
    \n
    Formula: BS * .1
  4. \n
  5. Calculating TDS based on Employment Typeemployment_type \n
    Condition: employment_type==\"Intern\"
    \n
    Amount: 1000
  6. \n
" + }, + { + "default": "0", + "fieldname": "round_to_the_nearest_integer", + "fieldtype": "Check", + "label": "Round to the Nearest Integer" + }, + { + "default": "0", + "depends_on": "eval:doc.type == \"Deduction\" && !doc.variable_based_on_taxable_salary", + "description": "If checked, the full amount will be deducted from taxable income before calculating income tax. Otherwise, it can be exempted via Employee Tax Exemption Declaration.", + "fieldname": "exempted_from_income_tax", + "fieldtype": "Check", + "label": "Exempted from Income Tax" + } + ], + "icon": "fa fa-flag", + "links": [], + "modified": "2020-04-24 14:50:28.994054", + "modified_by": "Administrator", + "module": "HR", + "name": "Salary Component", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Employee" + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_component/test_records.json b/erpnext/hr/doctype/salary_component/test_records.json index 7b22b481f36..104b44ffa1b 100644 --- a/erpnext/hr/doctype/salary_component/test_records.json +++ b/erpnext/hr/doctype/salary_component/test_records.json @@ -3,14 +3,12 @@ "doctype": "Salary Component", "salary_component": "_Test Basic Salary", "type": "Earning", - "is_payable": 1, "is_tax_applicable": 1 }, { "doctype": "Salary Component", "salary_component": "_Test Allowance", "type": "Earning", - "is_payable": 1, "is_tax_applicable": 1 }, { @@ -27,14 +25,12 @@ "doctype": "Salary Component", "salary_component": "Basic", "type": "Earning", - "is_payable": 1, "is_tax_applicable": 1 }, { "doctype": "Salary Component", "salary_component": "Leave Encashment", "type": "Earning", - "is_payable": 1, "is_tax_applicable": 1 } ] \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_component/test_salary_component.py b/erpnext/hr/doctype/salary_component/test_salary_component.py index 965cc9e9ffd..4f7db0c71ca 100644 --- a/erpnext/hr/doctype/salary_component/test_salary_component.py +++ b/erpnext/hr/doctype/salary_component/test_salary_component.py @@ -18,6 +18,5 @@ def create_salary_component(component_name, **args): "doctype": "Salary Component", "salary_component": component_name, "type": args.get("type") or "Earning", - "is_payable": args.get("is_payable") or 1, "is_tax_applicable": args.get("is_tax_applicable") or 1 }).insert() diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.json b/erpnext/hr/doctype/salary_detail/salary_detail.json index edf2786821e..545f56a0b60 100644 --- a/erpnext/hr/doctype/salary_detail/salary_detail.json +++ b/erpnext/hr/doctype/salary_detail/salary_detail.json @@ -1,765 +1,216 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-06-30 15:32:36.385111", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "creation": "2016-06-30 15:32:36.385111", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "salary_component", + "abbr", + "statistical_component", + "column_break_3", + "deduct_full_tax_on_selected_payroll_date", + "depends_on_payment_days", + "is_tax_applicable", + "exempted_from_income_tax", + "is_flexible_benefit", + "variable_based_on_taxable_salary", + "section_break_2", + "condition", + "amount_based_on_formula", + "formula", + "amount", + "do_not_include_in_total", + "default_amount", + "additional_amount", + "tax_on_flexible_benefit", + "tax_on_additional_salary", + "section_break_11", + "condition_and_formula_help" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "salary_component", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Component", - "length": 0, - "no_copy": 0, - "options": "Salary Component", - "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 - }, + "fieldname": "salary_component", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Component", + "options": "Salary Component", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "default": "", - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_from": "salary_component.salary_component_abbr", - "fetch_if_empty": 0, - "fieldname": "abbr", - "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": "Abbr", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 1, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fetch_from": "salary_component.salary_component_abbr", + "fieldname": "abbr", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Abbr", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", - "fetch_from": "salary_component.statistical_component", - "fetch_if_empty": 0, - "fieldname": "statistical_component", - "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": "Statistical Component", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", + "fetch_from": "salary_component.statistical_component", + "fieldname": "statistical_component", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Statistical Component" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "salary_component.is_tax_applicable", - "fetch_if_empty": 0, - "fieldname": "is_tax_applicable", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Tax Applicable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:doc.parentfield=='earnings'", + "fetch_from": "salary_component.is_tax_applicable", + "fieldname": "is_tax_applicable", + "fieldtype": "Check", + "label": "Is Tax Applicable", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "salary_component.is_flexible_benefit", - "fetch_if_empty": 0, - "fieldname": "is_flexible_benefit", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Flexible Benefit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:doc.parentfield=='earnings'", + "fetch_from": "salary_component.is_flexible_benefit", + "fieldname": "is_flexible_benefit", + "fieldtype": "Check", + "label": "Is Flexible Benefit", + "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, - "default": "", - "fetch_from": "salary_component.variable_based_on_taxable_salary", - "fetch_if_empty": 0, - "fieldname": "variable_based_on_taxable_salary", - "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": "Variable Based On Taxable Salary", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:doc.parentfield=='deductions'", + "fetch_from": "salary_component.variable_based_on_taxable_salary", + "fieldname": "variable_based_on_taxable_salary", + "fieldtype": "Check", + "label": "Variable Based On Taxable Salary", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_from": "salary_component.depends_on_payment_days", - "fetch_if_empty": 0, - "fieldname": "depends_on_payment_days", - "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": "Depends on Payment Days", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fetch_from": "salary_component.depends_on_payment_days", + "fieldname": "depends_on_payment_days", + "fieldtype": "Check", + "label": "Depends on Payment Days", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "deduct_full_tax_on_selected_payroll_date", - "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": "Deduct Full Tax on Selected Payroll Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "deduct_full_tax_on_selected_payroll_date", + "fieldtype": "Check", + "label": "Deduct Full Tax on Selected Payroll Date", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_flexible_benefit != 1", - "fetch_if_empty": 0, - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "depends_on": "eval:doc.is_flexible_benefit != 1", + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_if_empty": 0, - "fieldname": "condition", - "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": "Condition", - "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_on_submit": 1, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "condition", + "fieldtype": "Code", + "label": "Condition" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_from": "", - "fetch_if_empty": 0, - "fieldname": "amount_based_on_formula", - "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": "Amount based on formula", - "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 - }, + "default": "0", + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "amount_based_on_formula", + "fieldtype": "Check", + "label": "Amount based on formula" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "depends_on": "eval:doc.amount_based_on_formula!==0 && doc.parenttype==='Salary Structure'", - "description": "", - "fetch_if_empty": 0, - "fieldname": "formula", - "fieldtype": "Code", - "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": "Formula", - "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_on_submit": 1, + "depends_on": "eval:doc.amount_based_on_formula!==0 && doc.parenttype==='Salary Structure'", + "fieldname": "formula", + "fieldtype": "Code", + "in_list_view": 1, + "label": "Formula" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.amount_based_on_formula!==1 || doc.parenttype==='Salary Slip'", - "fetch_if_empty": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.amount_based_on_formula!==1 || doc.parenttype==='Salary Slip'", + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "Company:company:default_currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "do_not_include_in_total", - "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": "Do not include in total", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "do_not_include_in_total", + "fieldtype": "Check", + "label": "Do not include in total" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_if_empty": 0, - "fieldname": "default_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": "Default Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "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 - }, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "default_amount", + "fieldtype": "Currency", + "label": "Default Amount", + "options": "Company:company:default_currency", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_from": "", - "fetch_if_empty": 0, - "fieldname": "additional_amount", - "fieldtype": "Currency", - "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": "Additional Amount", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "additional_amount", + "fieldtype": "Currency", + "hidden": 1, + "label": "Additional Amount", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", - "fetch_if_empty": 0, - "fieldname": "tax_on_flexible_benefit", - "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": "Tax on flexible benefit", - "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 - }, + "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", + "fieldname": "tax_on_flexible_benefit", + "fieldtype": "Currency", + "label": "Tax on flexible benefit", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", - "fetch_if_empty": 0, - "fieldname": "tax_on_additional_salary", - "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": "Tax on additional salary", - "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 - }, + "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", + "fieldname": "tax_on_additional_salary", + "fieldtype": "Currency", + "label": "Tax on additional salary", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_if_empty": 0, - "fieldname": "section_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "section_break_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_if_empty": 0, - "fieldname": "condition_and_formula_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": "Condition and Formula Help", - "length": 0, - "no_copy": 0, - "options": "

Condition and Formula Help

\n\n

Notes:

\n\n
    \n
  1. Use field base for using base salary of the Employee
  2. \n
  3. Use Salary Component abbreviations in conditions and formulas. BS = Basic Salary
  4. \n
  5. Use field name for employee details in conditions and formulas. Employment Type = employment_typeBranch = branch
  6. \n
  7. Use field name from Salary Slip in conditions and formulas. Payment Days = payment_daysLeave without pay = leave_without_pay
  8. \n
  9. Direct Amount can also be entered based on Condtion. See example 3
\n\n

Examples

\n
    \n
  1. Calculating Basic Salary based on base\n
    Condition: base < 10000
    \n
    Formula: base * .2
  2. \n
  3. Calculating HRA based on Basic SalaryBS \n
    Condition: BS > 2000
    \n
    Formula: BS * .1
  4. \n
  5. Calculating TDS based on Employment Typeemployment_type \n
    Condition: employment_type==\"Intern\"
    \n
    Amount: 1000
  6. \n
", - "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 + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "condition_and_formula_help", + "fieldtype": "HTML", + "label": "Condition and Formula Help", + "options": "

Condition and Formula Help

\n\n

Notes:

\n\n
    \n
  1. Use field base for using base salary of the Employee
  2. \n
  3. Use Salary Component abbreviations in conditions and formulas. BS = Basic Salary
  4. \n
  5. Use field name for employee details in conditions and formulas. Employment Type = employment_typeBranch = branch
  6. \n
  7. Use field name from Salary Slip in conditions and formulas. Payment Days = payment_daysLeave without pay = leave_without_pay
  8. \n
  9. Direct Amount can also be entered based on Condtion. See example 3
\n\n

Examples

\n
    \n
  1. Calculating Basic Salary based on base\n
    Condition: base < 10000
    \n
    Formula: base * .2
  2. \n
  3. Calculating HRA based on Basic SalaryBS \n
    Condition: BS > 2000
    \n
    Formula: BS * .1
  4. \n
  5. Calculating TDS based on Employment Typeemployment_type \n
    Condition: employment_type==\"Intern\"
    \n
    Amount: 1000
  6. \n
" + }, + { + "default": "0", + "depends_on": "eval:doc.parentfield=='deductions'", + "fetch_from": "salary_component.exempted_from_income_tax", + "fieldname": "exempted_from_income_tax", + "fieldtype": "Check", + "label": "Exempted from Income Tax", + "read_only": 1 } - ], - "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-05-11 17:33:08.508653", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Detail", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-04-24 20:00:16.475295", + "modified_by": "Administrator", + "module": "HR", + "name": "Salary Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 6157daff1e0..dda15014166 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -450,7 +450,8 @@ class SalarySlip(TransactionBase): 'is_flexible_benefit': struct_row.is_flexible_benefit, 'variable_based_on_taxable_salary': struct_row.variable_based_on_taxable_salary, 'deduct_full_tax_on_selected_payroll_date': struct_row.deduct_full_tax_on_selected_payroll_date, - 'additional_amount': amount if struct_row.get("is_additional_component") else 0 + 'additional_amount': amount if struct_row.get("is_additional_component") else 0, + 'exempted_from_income_tax': struct_row.exempted_from_income_tax }) else: if struct_row.get("is_additional_component"): @@ -481,10 +482,12 @@ class SalarySlip(TransactionBase): return self.calculate_variable_tax(payroll_period, tax_component) def calculate_variable_tax(self, payroll_period, tax_component): + # get Tax slab from salary structure assignment for the employee and payroll period + tax_slab = self.get_income_tax_slabs(payroll_period) + # get remaining numbers of sub-period (period for which one salary is processed) remaining_sub_periods = get_period_factor(self.employee, self.start_date, self.end_date, self.payroll_frequency, payroll_period)[1] - # get taxable_earnings, paid_taxes for previous period previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date, self.start_date) previous_total_paid_taxes = self.get_tax_paid_in_period(payroll_period.start_date, self.start_date, tax_component) @@ -506,23 +509,27 @@ class SalarySlip(TransactionBase): unclaimed_taxable_benefits += current_taxable_earnings_for_payment_days.flexi_benefits # Total exemption amount based on tax exemption declaration - total_exemption_amount, other_incomes = self.get_total_exemption_amount_and_other_incomes(payroll_period) + total_exemption_amount = self.get_total_exemption_amount(payroll_period, tax_slab) + + #Employee Other Incomes + other_incomes = self.get_income_form_other_sources(payroll_period) or 0.0 # Total taxable earnings including additional and other incomes total_taxable_earnings = previous_taxable_earnings + current_structured_taxable_earnings + future_structured_taxable_earnings \ + current_additional_earnings + other_incomes + unclaimed_taxable_benefits - total_exemption_amount - + # Total taxable earnings without additional earnings with full tax total_taxable_earnings_without_full_tax_addl_components = total_taxable_earnings - current_additional_earnings_with_full_tax # Structured tax amount - total_structured_tax_amount = self.calculate_tax_by_tax_slab(payroll_period, total_taxable_earnings_without_full_tax_addl_components) + total_structured_tax_amount = self.calculate_tax_by_tax_slab( + total_taxable_earnings_without_full_tax_addl_components, tax_slab) current_structured_tax_amount = (total_structured_tax_amount - previous_total_paid_taxes) / remaining_sub_periods - + # Total taxable earnings with additional earnings with full tax full_tax_on_additional_earnings = 0.0 if current_additional_earnings_with_full_tax: - total_tax_amount = self.calculate_tax_by_tax_slab(payroll_period, total_taxable_earnings) + total_tax_amount = self.calculate_tax_by_tax_slab(total_taxable_earnings, tax_slab) full_tax_on_additional_earnings = total_tax_amount - total_structured_tax_amount current_tax_amount = current_structured_tax_amount + full_tax_on_additional_earnings @@ -531,12 +538,30 @@ class SalarySlip(TransactionBase): return current_tax_amount + def get_income_tax_slabs(self, payroll_period): + income_tax_slab, ss_assignment_name = frappe.db.get_value("Salary Structure Assignment", + {"employee": self.employee, "salary_structure": self.salary_structure, "docstatus": 1}, ["income_tax_slab", 'name']) + + if not income_tax_slab: + frappe.throw(_("Income Tax Slab not set in Salary Structure Assignment: {0}").format(ss_assignment_name)) + + income_tax_slab_doc = frappe.get_doc("Income Tax Slab", income_tax_slab) + if income_tax_slab_doc.disabled: + frappe.throw(_("Income Tax Slab: {0} is disabled").format(income_tax_slab)) + + if getdate(income_tax_slab_doc.effective_from) > getdate(payroll_period.start_date): + frappe.throw(_("Income Tax Slab must be effective on or before Payroll Period Start Date: {0}") + .format(payroll_period.start_date)) + + return income_tax_slab_doc + + def get_taxable_earnings_for_prev_period(self, start_date, end_date): taxable_earnings = frappe.db.sql(""" select sum(sd.amount) from `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name - where + where sd.parentfield='earnings' and sd.is_tax_applicable=1 and is_flexible_benefit=0 @@ -549,7 +574,28 @@ class SalarySlip(TransactionBase): "from_date": start_date, "to_date": end_date }) - return flt(taxable_earnings[0][0]) if taxable_earnings else 0 + taxable_earnings = flt(taxable_earnings[0][0]) if taxable_earnings else 0 + + exempted_amount = frappe.db.sql(""" + select sum(sd.amount) + from + `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name + where + sd.parentfield='deductions' + and sd.exempted_from_income_tax=1 + and is_flexible_benefit=0 + and ss.docstatus=1 + and ss.employee=%(employee)s + and ss.start_date between %(from_date)s and %(to_date)s + and ss.end_date between %(from_date)s and %(to_date)s + """, { + "employee": self.employee, + "from_date": start_date, + "to_date": end_date + }) + exempted_amount = flt(exempted_amount[0][0]) if exempted_amount else 0 + + return taxable_earnings - exempted_amount def get_tax_paid_in_period(self, start_date, end_date, tax_component): # find total_tax_paid, tax paid for benefit, additional_salary @@ -609,6 +655,13 @@ class SalarySlip(TransactionBase): else: taxable_earnings += amount + for ded in self.deductions: + if ded.exempted_from_income_tax: + amount = ded.amount + if based_on_payment_days: + amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)[0] + taxable_earnings -= flt(amount) + return frappe._dict({ "taxable_earnings": taxable_earnings, "additional_income": additional_income, @@ -671,40 +724,63 @@ class SalarySlip(TransactionBase): return total_benefits_paid - total_benefits_claimed - def get_total_exemption_amount_and_other_incomes(self, payroll_period): - total_exemption_amount, other_incomes = 0, 0 - if self.deduct_tax_for_unsubmitted_tax_exemption_proof: - exemption_proof = frappe.db.get_value("Employee Tax Exemption Proof Submission", - {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, - ["exemption_amount", "income_from_other_sources"]) - if exemption_proof: - total_exemption_amount, other_incomes = exemption_proof - else: - declaration = frappe.db.get_value("Employee Tax Exemption Declaration", - {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, - ["total_exemption_amount", "income_from_other_sources"]) - if declaration: - total_exemption_amount, other_incomes = declaration + def get_total_exemption_amount(self, payroll_period, tax_slab): + total_exemption_amount = 0 + if tax_slab.allow_tax_exemption: + if self.deduct_tax_for_unsubmitted_tax_exemption_proof: + exemption_proof = frappe.db.get_value("Employee Tax Exemption Proof Submission", + {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, + ["exemption_amount"]) + if exemption_proof: + total_exemption_amount = exemption_proof + else: + declaration = frappe.db.get_value("Employee Tax Exemption Declaration", + {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, + ["total_exemption_amount"]) + if declaration: + total_exemption_amount = declaration - return total_exemption_amount, other_incomes + total_exemption_amount += flt(tax_slab.standard_tax_exemption_amount) - def calculate_tax_by_tax_slab(self, payroll_period, annual_taxable_earning): - payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period) - annual_taxable_earning -= flt(payroll_period_obj.standard_tax_exemption_amount) + return total_exemption_amount + + def get_income_form_other_sources(self, payroll_period): + return frappe.get_all("Employee Other Income", + filters={ + "employee": self.employee, + "payroll_period": payroll_period.name, + "company": self.company, + "docstatus": 1 + }, + fields="SUM(amount) as total_amount" + )[0].total_amount + + def calculate_tax_by_tax_slab(self, annual_taxable_earning, tax_slab): data = self.get_data_for_eval() data.update({"annual_taxable_earning": annual_taxable_earning}) - taxable_amount = 0 - for slab in payroll_period_obj.taxable_salary_slabs: + tax_amount = 0 + for slab in tax_slab.slabs: if slab.condition and not self.eval_tax_slab_condition(slab.condition, data): continue if not slab.to_amount and annual_taxable_earning > slab.from_amount: - taxable_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01 + tax_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01 continue if annual_taxable_earning > slab.from_amount and annual_taxable_earning < slab.to_amount: - taxable_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01 + tax_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01 elif annual_taxable_earning > slab.from_amount and annual_taxable_earning > slab.to_amount: - taxable_amount += (slab.to_amount - slab.from_amount) * slab.percent_deduction * .01 - return taxable_amount + tax_amount += (slab.to_amount - slab.from_amount) * slab.percent_deduction * .01 + + # other taxes and charges on income tax + for d in tax_slab.other_taxes_and_charges: + if flt(d.min_taxable_income) and flt(d.min_taxable_income) > tax_amount: + continue + + if flt(d.max_taxable_income) and flt(d.max_taxable_income) < tax_amount: + continue + + tax_amount += tax_amount * flt(d.percent) / 100 + + return tax_amount def eval_tax_slab_condition(self, condition, data): try: diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index 16a75f473f4..7572e00359a 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -47,10 +47,7 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.payment_days, no_of_days[0]) self.assertEqual(ss.earnings[0].amount, 50000) self.assertEqual(ss.earnings[1].amount, 3000) - self.assertEqual(ss.deductions[0].amount, 5000) - self.assertEqual(ss.deductions[1].amount, 5000) self.assertEqual(ss.gross_pay, 78000) - self.assertEqual(ss.net_pay, 67418.0) def test_salary_slip_with_holidays_excluded(self): no_of_days = self.get_no_of_days() @@ -67,10 +64,7 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.earnings[0].amount, 50000) self.assertEqual(ss.earnings[0].default_amount, 50000) self.assertEqual(ss.earnings[1].amount, 3000) - self.assertEqual(ss.deductions[0].amount, 5000) - self.assertEqual(ss.deductions[1].amount, 5000) self.assertEqual(ss.gross_pay, 78000) - self.assertEqual(ss.net_pay, 67418.0) def test_payment_days(self): no_of_days = self.get_no_of_days() @@ -80,8 +74,8 @@ class TestSalarySlip(unittest.TestCase): # set joinng date in the same month make_employee("test_employee@salary.com") if getdate(nowdate()).day >= 15: - date_of_joining = getdate(add_days(nowdate(),-10)) relieving_date = getdate(add_days(nowdate(),-10)) + date_of_joining = getdate(add_days(nowdate(),-10)) elif getdate(nowdate()).day < 15 and getdate(nowdate()).day >= 5: date_of_joining = getdate(add_days(nowdate(),-3)) relieving_date = getdate(add_days(nowdate(),-3)) @@ -131,9 +125,7 @@ class TestSalarySlip(unittest.TestCase): def test_email_salary_slip(self): frappe.db.sql("delete from `tabEmail Queue`") - hr_settings = frappe.get_doc("HR Settings", "HR Settings") - hr_settings.email_salary_slip_to_employee = 1 - hr_settings.save() + frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 1) make_employee("test_employee@salary.com") ss = make_employee_salary_slip("test_employee@salary.com", "Monthly") @@ -183,8 +175,11 @@ class TestSalarySlip(unittest.TestCase): # as per assigned salary structure 40500 in monthly salary so 236000*5/100/12 frappe.db.sql("""delete from `tabPayroll Period`""") frappe.db.sql("""delete from `tabSalary Component`""") + payroll_period = create_payroll_period() - create_tax_slab(payroll_period) + + create_tax_slab(payroll_period, allow_tax_exemption=True) + employee = make_employee("test_tax@salary.slip") delete_docs = [ "Salary Slip", @@ -210,8 +205,7 @@ class TestSalarySlip(unittest.TestCase): payroll_period, deduct_random=False) tax_paid = get_tax_paid_in_period(employee) - # total taxable income 586000, 250000 @ 5%, 86000 @ 20% ie. 12500 + 17200 - annual_tax = 113568 + annual_tax = 113589.0 try: self.assertEqual(tax_paid, annual_tax) except AssertionError: @@ -235,8 +229,7 @@ class TestSalarySlip(unittest.TestCase): raise # Submit proof for total 120000 - data["proof-1"] = create_proof_submission(employee, payroll_period, 50000) - data["proof-2"] = create_proof_submission(employee, payroll_period, 70000) + data["proof"] = create_proof_submission(employee, payroll_period, 120000) # Submit benefit claim for total 50000 data["benefit-1"] = create_benefit_claim(employee, payroll_period, 15000, "Medical Allowance") @@ -250,7 +243,7 @@ class TestSalarySlip(unittest.TestCase): # total taxable income 416000, 166000 @ 5% ie. 8300 try: - self.assertEqual(tax_paid, 88608) + self.assertEqual(tax_paid, 82389.0) except AssertionError: print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") raise @@ -265,7 +258,7 @@ class TestSalarySlip(unittest.TestCase): # total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200 tax_paid = get_tax_paid_in_period(employee) try: - self.assertEqual(tax_paid, 121211) + self.assertEqual(tax_paid, annual_tax) except AssertionError: print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") raise @@ -307,6 +300,7 @@ def make_employee_salary_slip(user, payroll_frequency, salary_structure=None): from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure if not salary_structure: salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip" + employee = frappe.db.get_value("Employee", {"user_id": user}) salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee) salary_slip = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})}) @@ -431,17 +425,15 @@ def make_deduction_salary_component(setup=False, test_tax=False): { "salary_component": 'Professional Tax', "abbr":'PT', - "condition": 'base > 10000', - "formula": 'base*.1', "type": "Deduction", - "amount_based_on_formula": 1 + "amount": 200, + "exempted_from_income_tax": 1 + }, { "salary_component": 'TDS', "abbr":'T', - "formula": 'base*.1', "type": "Deduction", - "amount_based_on_formula": 1, "depends_on_payment_days": 0, "variable_based_on_taxable_salary": 1, "round_to_the_nearest_integer": 1 @@ -452,9 +444,7 @@ def make_deduction_salary_component(setup=False, test_tax=False): "salary_component": 'TDS', "abbr":'T', "condition": 'employment_type=="Intern"', - "formula": 'base*.1', "type": "Deduction", - "amount_based_on_formula": 1, "round_to_the_nearest_integer": 1 }) if setup or test_tax: @@ -510,29 +500,47 @@ def create_benefit_claim(employee, payroll_period, amount, component): }).submit() return claim_date -def create_tax_slab(payroll_period): - data = [ +def create_tax_slab(payroll_period, effective_date = None, allow_tax_exemption = False, dont_submit = False): + if frappe.db.exists("Income Tax Slab", "Tax Slab: " + payroll_period.name): + return + + slabs = [ { "from_amount": 250000, "to_amount": 500000, - "percent_deduction": 5.2, + "percent_deduction": 5, "condition": "annual_taxable_earning > 500000" }, { "from_amount": 500001, "to_amount": 1000000, - "percent_deduction": 20.8 + "percent_deduction": 20 }, { "from_amount": 1000001, - "percent_deduction": 31.2 + "percent_deduction": 30 } ] - payroll_period.taxable_salary_slabs = [] - for item in data: - payroll_period.append("taxable_salary_slabs", item) - payroll_period.standard_tax_exemption_amount = 52500 - payroll_period.save() + + income_tax_slab = frappe.new_doc("Income Tax Slab") + income_tax_slab.name = "Tax Slab: " + payroll_period.name + income_tax_slab.effective_from = effective_date or add_days(payroll_period.start_date, -2) + + if allow_tax_exemption: + income_tax_slab.allow_tax_exemption = 1 + income_tax_slab.standard_tax_exemption_amount = 50000 + + for item in slabs: + income_tax_slab.append("slabs", item) + + income_tax_slab.append("other_taxes_and_charges", { + "description": "cess", + "percent": 4 + }) + + income_tax_slab.save() + if not dont_submit: + income_tax_slab.submit() def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True): deducted_dates = [] diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js index 672be0c2fc2..2f2c815d70c 100755 --- a/erpnext/hr/doctype/salary_structure/salary_structure.js +++ b/erpnext/hr/doctype/salary_structure/salary_structure.js @@ -82,6 +82,7 @@ frappe.ui.form.on('Salary Structure', { {fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")}, {fieldname:'base_variable', fieldtype:'Section Break'}, {fieldname:'from_date', fieldtype:'Date', label: __('From Date'), "reqd": 1}, + {fieldname:'income_tax_slab', fieldtype:'Link', label: __('Income Tax Slab'), options: 'Income Tax Slab'}, {fieldname:'base_col_br', fieldtype:'Column Break'}, {fieldname:'base', fieldtype:'Currency', label: __('Base')}, {fieldname:'variable', fieldtype:'Currency', label: __('Variable')} diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py index 568277f8a73..df76458fe02 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/salary_structure.py @@ -16,6 +16,7 @@ class SalaryStructure(Document): self.validate_amount() self.strip_condition_and_formula_fields() self.validate_max_benefits_with_flexi() + self.validate_component_based_on_tax_slab() def set_missing_values(self): overwritten_fields = ["depends_on_payment_days", "variable_based_on_taxable_salary", "is_tax_applicable", "is_flexible_benefit"] @@ -34,6 +35,12 @@ class SalaryStructure(Document): for fieldname in overwritten_fields_if_missing: d.set(fieldname, component_default_value.get(fieldname)) + def validate_component_based_on_tax_slab(self): + for row in self.deductions: + if row.variable_based_on_taxable_salary and (row.amount or row.formula): + frappe.throw(_("Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary") + .format(row.idx, row.salary_component)) + def validate_amount(self): if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet: frappe.throw(_("Net pay cannot be negative")) @@ -82,21 +89,23 @@ class SalaryStructure(Document): @frappe.whitelist() def assign_salary_structure(self, company=None, grade=None, department=None, designation=None,employee=None, - from_date=None, base=None,variable=None): + from_date=None, base=None, variable=None, income_tax_slab=None): employees = self.get_employees(company= company, grade= grade,department= department,designation= designation,name=employee) if employees: if len(employees) > 20: frappe.enqueue(assign_salary_structure_for_employees, timeout=600, - employees=employees, salary_structure=self,from_date=from_date, base=base,variable=variable) + employees=employees, salary_structure=self,from_date=from_date, + base=base, variable=variable, income_tax_slab=income_tax_slab) else: - assign_salary_structure_for_employees(employees, self, from_date=from_date, base=base,variable=variable) + assign_salary_structure_for_employees(employees, self, from_date=from_date, + base=base, variable=variable, income_tax_slab=income_tax_slab) else: frappe.msgprint(_("No Employee Found")) -def assign_salary_structure_for_employees(employees, salary_structure, from_date=None, base=None,variable=None): +def assign_salary_structure_for_employees(employees, salary_structure, from_date=None, base=None, variable=None, income_tax_slab=None): salary_structures_assignments = [] existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date) count=0 @@ -105,7 +114,8 @@ def assign_salary_structure_for_employees(employees, salary_structure, from_date continue count +=1 - salary_structures_assignment = create_salary_structures_assignment(employee, salary_structure, from_date, base, variable) + salary_structures_assignment = create_salary_structures_assignment(employee, + salary_structure, from_date, base, variable, income_tax_slab) salary_structures_assignments.append(salary_structures_assignment) frappe.publish_progress(count*100/len(set(employees) - set(existing_assignments_for)), title = _("Assigning Structures...")) @@ -113,7 +123,7 @@ def assign_salary_structure_for_employees(employees, salary_structure, from_date frappe.msgprint(_("Structures have been assigned successfully")) -def create_salary_structures_assignment(employee, salary_structure, from_date, base, variable): +def create_salary_structures_assignment(employee, salary_structure, from_date, base, variable, income_tax_slab=None): assignment = frappe.new_doc("Salary Structure Assignment") assignment.employee = employee assignment.salary_structure = salary_structure.name @@ -121,6 +131,7 @@ def create_salary_structures_assignment(employee, salary_structure, from_date, b assignment.from_date = from_date assignment.base = base assignment.variable = variable + assignment.income_tax_slab = income_tax_slab assignment.save(ignore_permissions = True) assignment.submit() return assignment.name diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index f67a189ee71..3e55a1a068d 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -9,8 +9,9 @@ from frappe.utils.make_random import get_random from frappe.utils import nowdate, add_days, add_years, getdate, add_months from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.hr.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\ - make_deduction_salary_component, make_employee_salary_slip + make_deduction_salary_component, make_employee_salary_slip, create_tax_slab from erpnext.hr.doctype.employee.test_employee import make_employee +from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_payroll_period test_dependencies = ["Fiscal Year"] @@ -70,10 +71,8 @@ class TestSalaryStructure(unittest.TestCase): self.assertEqual(sal_slip.get("earnings")[1].amount, 3000) self.assertEqual(sal_slip.get("earnings")[2].amount, 25000) self.assertEqual(sal_slip.get("gross_pay"), 78000) - self.assertEqual(sal_slip.get("deductions")[0].amount, 5000) - self.assertEqual(sal_slip.get("deductions")[1].amount, 5000) - self.assertEqual(sal_slip.get("total_deduction"), 10000) - self.assertEqual(sal_slip.get("net_pay"), 68000) + self.assertEqual(sal_slip.get("deductions")[0].amount, 200) + self.assertEqual(sal_slip.get("net_pay"), 78000 - sal_slip.get("total_deduction")) def test_whitespaces_in_formula_conditions_fields(self): salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True) @@ -111,6 +110,7 @@ class TestSalaryStructure(unittest.TestCase): def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None, test_tax=False): if test_tax: frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure)) + if not frappe.db.exists('Salary Structure', salary_structure): details = { "doctype": "Salary Structure", @@ -123,7 +123,8 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do } if other_details and isinstance(other_details, dict): details.update(other_details) - salary_structure_doc = frappe.get_doc(details).insert() + salary_structure_doc = frappe.get_doc(details) + salary_structure_doc.insert() if not dont_submit: salary_structure_doc.submit() else: @@ -138,13 +139,18 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do def create_salary_structure_assignment(employee, salary_structure, from_date=None): if frappe.db.exists("Salary Structure Assignment", {"employee": employee}): frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""",(employee)) + + payroll_period = create_payroll_period() + create_tax_slab(payroll_period, allow_tax_exemption=True) + salary_structure_assignment = frappe.new_doc("Salary Structure Assignment") salary_structure_assignment.employee = employee salary_structure_assignment.base = 50000 salary_structure_assignment.variable = 5000 - salary_structure_assignment.from_date = from_date or add_months(nowdate(), -1) + salary_structure_assignment.from_date = from_date or add_days(nowdate(), -1) salary_structure_assignment.salary_structure = salary_structure salary_structure_assignment.company = erpnext.get_default_company() salary_structure_assignment.save(ignore_permissions=True) + salary_structure_assignment.income_tax_slab = "Tax Slab: _Test Payroll Period" salary_structure_assignment.submit() return salary_structure_assignment diff --git a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.js b/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.js index 56a05e04956..818e853154d 100644 --- a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.js +++ b/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.js @@ -20,6 +20,16 @@ frappe.ui.form.on('Salary Structure Assignment', { } } }); + + frm.set_query("income_tax_slab", function() { + return { + filters: { + company: frm.doc.company, + docstatus: 1, + disabled: 0 + } + } + }); }, employee: function(frm) { if(frm.doc.employee){ diff --git a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json index 136eb0f8053..0098aa8ec80 100644 --- a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json +++ b/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json @@ -10,11 +10,12 @@ "employee", "employee_name", "department", - "designation", + "company", "column_break_6", + "designation", "salary_structure", "from_date", - "company", + "income_tax_slab", "section_break_7", "base", "column_break_9", @@ -113,11 +114,17 @@ "options": "Salary Structure Assignment", "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "income_tax_slab", + "fieldtype": "Link", + "label": "Income Tax Slab", + "options": "Income Tax Slab" } ], "is_submittable": 1, "links": [], - "modified": "2019-12-31 17:05:28.637510", + "modified": "2020-04-25 18:24:23.617088", "modified_by": "Administrator", "module": "HR", "name": "Salary Structure Assignment", diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 4f5653dc67e..5c95e000f9c 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -9,6 +9,8 @@ from frappe.model.document import Document from frappe.desk.form import assign_to from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee +class DuplicateDeclarationError(frappe.ValidationError): pass + class EmployeeBoardingController(Document): ''' Create the project and the task for the boarding process @@ -226,6 +228,17 @@ def get_employee_leave_policy(employee): else: frappe.throw(_("Please set leave policy for employee {0} in Employee / Grade record").format(employee)) +def validate_duplicate_exemption_for_payroll_period(doctype, docname, payroll_period, employee): + existing_record = frappe.db.exists(doctype, { + "payroll_period": payroll_period, + "employee": employee, + 'docstatus': ['<', 2], + 'name': ['!=', docname] + }) + if existing_record: + frappe.throw(_("{0} already exists for employee {1} and period {2}") + .format(doctype, employee, payroll_period), DuplicateDeclarationError) + def validate_tax_declaration(declarations): subcategories = [] for d in declarations: diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ad02a68faaa..d8ef786a15f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -659,3 +659,4 @@ erpnext.patches.v12_0.set_correct_status_for_expense_claim erpnext.patches.v12_0.set_updated_purpose_in_pick_list erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign +erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 diff --git a/erpnext/patches/v11_0/set_salary_component_properties.py b/erpnext/patches/v11_0/set_salary_component_properties.py index fa3605ba5f1..83fb53d2a73 100644 --- a/erpnext/patches/v11_0/set_salary_component_properties.py +++ b/erpnext/patches/v11_0/set_salary_component_properties.py @@ -5,8 +5,7 @@ def execute(): frappe.reload_doc('hr', 'doctype', 'salary_detail') frappe.reload_doc('hr', 'doctype', 'salary_component') - frappe.db.sql("update `tabSalary Component` set is_payable=1, is_tax_applicable=1 where type='Earning'") - frappe.db.sql("update `tabSalary Component` set is_payable=0 where type='Deduction'") + frappe.db.sql("update `tabSalary Component` set is_tax_applicable=1 where type='Earning'") frappe.db.sql("""update `tabSalary Component` set variable_based_on_taxable_salary=1 where type='Deduction' and name in ('TDS', 'Tax Deducted at Source')""") diff --git a/erpnext/patches/v13_0/__init__.py b/erpnext/patches/v13_0/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py new file mode 100644 index 00000000000..a6aefac12ad --- /dev/null +++ b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py @@ -0,0 +1,99 @@ +# 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(): + if not frappe.db.table_exists("Payroll Period"): + return + + for doctype in ("income_tax_slab", "salary_structure_assignment", "employee_other_income"): + frappe.reload_doc("hr", "doctype", doctype) + + + for company in frappe.get_all("Company"): + payroll_periods = frappe.db.sql(""" + SELECT + name, start_date, end_date, standard_tax_exemption_amount + FROM + `tabPayroll Period` + WHERE company=%s + ORDER BY start_date DESC + """, company.name, as_dict = 1) + + for i, period in enumerate(payroll_periods): + income_tax_slab = frappe.new_doc("Income Tax Slab") + income_tax_slab.name = "Tax Slab:" + period.name + + if i == 0: + income_tax_slab.disabled = 0 + else: + income_tax_slab.disabled = 1 + + income_tax_slab.effective_from = period.start_date + income_tax_slab.company = company.name + income_tax_slab.allow_tax_exemption = 1 + income_tax_slab.standard_tax_exemption_amount = period.standard_tax_exemption_amount + + income_tax_slab.flags.ignore_mandatory = True + income_tax_slab.submit() + + frappe.db.sql( + """ UPDATE `tabTaxable Salary Slab` + SET parent = %s , parentfield = 'slabs' , parenttype = "Income Tax Slab" + WHERE parent = %s + """, (income_tax_slab.name, period.name), as_dict = 1) + + if i == 0: + frappe.db.sql(""" + UPDATE + `tabSalary Structure Assignment` + set + income_tax_slab = %s + where + company = %s + and from_date >= %s + and docstatus < 2 + """, (income_tax_slab.name, company.name, period.start_date)) + + # move other incomes to separate document + migrated = [] + proofs = frappe.get_all("Employee Tax Exemption Proof Submission", + filters = {'docstatus': 1}, + fields =['payroll_period', 'employee', 'company', 'income_from_other_sources'] + ) + for proof in proofs: + if proof.income_from_other_sources: + employee_other_income = frappe.new_doc("Employee Other Income") + employee_other_income.employee = proof.employee + employee_other_income.payroll_period = proof.payroll_period + employee_other_income.company = proof.company + employee_other_income.amount = proof.income_from_other_sources + + try: + employee_other_income.submit() + migrated.append([proof.employee, proof.payroll_period]) + except: + pass + + declerations = frappe.get_all("Employee Tax Exemption Declaration", + filters = {'docstatus': 1}, + fields =['payroll_period', 'employee', 'company', 'income_from_other_sources'] + ) + + for declaration in declerations: + if declaration.income_from_other_sources \ + and [declaration.employee, declaration.payroll_period] not in migrated: + employee_other_income = frappe.new_doc("Employee Other Income") + employee_other_income.employee = declaration.employee + employee_other_income.payroll_period = declaration.payroll_period + employee_other_income.company = declaration.company + employee_other_income.amount = declaration.income_from_other_sources + + try: + employee_other_income.submit() + except: + pass diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 1957e7fecca..da43cc65553 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -530,12 +530,18 @@ def make_fixtures(company=None): def set_salary_components(docs): docs.extend([ - {'doctype': 'Salary Component', 'salary_component': 'Professional Tax', 'description': 'Professional Tax', 'type': 'Deduction'}, - {'doctype': 'Salary Component', 'salary_component': 'Provident Fund', 'description': 'Provident fund', 'type': 'Deduction'}, - {'doctype': 'Salary Component', 'salary_component': 'House Rent Allowance', 'description': 'House Rent Allowance', 'type': 'Earning'}, - {'doctype': 'Salary Component', 'salary_component': 'Basic', 'description': 'Basic', 'type': 'Earning'}, - {'doctype': 'Salary Component', 'salary_component': 'Arrear', 'description': 'Arrear', 'type': 'Earning'}, - {'doctype': 'Salary Component', 'salary_component': 'Leave Encashment', 'description': 'Leave Encashment', 'type': 'Earning'} + {'doctype': 'Salary Component', 'salary_component': 'Professional Tax', + 'description': 'Professional Tax', 'type': 'Deduction', 'exempted_from_income_tax': 1}, + {'doctype': 'Salary Component', 'salary_component': 'Provident Fund', + 'description': 'Provident fund', 'type': 'Deduction', 'is_tax_applicable': 1}, + {'doctype': 'Salary Component', 'salary_component': 'House Rent Allowance', + 'description': 'House Rent Allowance', 'type': 'Earning', 'is_tax_applicable': 1}, + {'doctype': 'Salary Component', 'salary_component': 'Basic', + 'description': 'Basic', 'type': 'Earning', 'is_tax_applicable': 1}, + {'doctype': 'Salary Component', 'salary_component': 'Arrear', + 'description': 'Arrear', 'type': 'Earning', 'is_tax_applicable': 1}, + {'doctype': 'Salary Component', 'salary_component': 'Leave Encashment', + 'description': 'Leave Encashment', 'type': 'Earning', 'is_tax_applicable': 1} ]) def set_tax_withholding_category(company): From b724fec6c96033547add180d3da95226b0dafe8e Mon Sep 17 00:00:00 2001 From: marination Date: Sun, 26 Apr 2020 17:16:21 +0530 Subject: [PATCH 390/929] fix: Remove callback outside if condition --- erpnext/stock/doctype/stock_entry/stock_entry.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 039aa777818..67baee242f2 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -305,10 +305,9 @@ frappe.ui.form.on('Stock Entry', { callback: function(r) { if (!r.exe && r.message){ frappe.model.set_value(cdt, cdn, "serial_no", r.message); - - if (callback) { - callback(); - } + } + if (callback) { + callback(); } } }); @@ -454,8 +453,9 @@ frappe.ui.form.on('Stock Entry', { frappe.ui.form.on('Stock Entry Detail', { qty: function(frm, cdt, cdn) { - frm.events.set_basic_rate(frm, cdt, cdn); - frm.events.set_serial_no(frm, cdt, cdn); + frm.events.set_serial_no(frm, cdt, cdn, () => { + frm.events.set_basic_rate(frm, cdt, cdn); + }); }, conversion_factor: function(frm, cdt, cdn) { From 5bfdf0af4d2225bd54cb4182bbfb60e92dbc3657 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sun, 26 Apr 2020 20:10:16 +0530 Subject: [PATCH 391/929] feat: Allow tax withholding category selection at invoice level (#20871) * feat: Allow tax withholding category selection at invoice level * fix: Linitng fixes * feat: TDS calculation using common PAN * fix: Add provision to deduct Lower TDS in purchase invoice * fix: Consider only ref docs company while computing TDS * fix: Default permission fixes * fix: Add validation for dates in fiscal year * fix: Undefined variable --- .../purchase_invoice/purchase_invoice.js | 15 +- .../purchase_invoice/purchase_invoice.json | 11 +- .../purchase_invoice/purchase_invoice.py | 2 +- .../tax_withholding_category.py | 128 +++++++++++++--- .../tds_computation_summary.py | 11 +- erpnext/patches.txt | 1 + .../add_permission_in_lower_deduction.py | 13 ++ .../lower_deduction_certificate/__init__.py | 0 .../lower_deduction_certificate.js | 8 + .../lower_deduction_certificate.json | 138 ++++++++++++++++++ .../lower_deduction_certificate.py | 26 ++++ .../test_lower_deduction_certificate.py | 10 ++ erpnext/regional/india/setup.py | 2 +- 13 files changed, 334 insertions(+), 31 deletions(-) create mode 100644 erpnext/patches/v12_0/add_permission_in_lower_deduction.py create mode 100644 erpnext/regional/doctype/lower_deduction_certificate/__init__.py create mode 100644 erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.js create mode 100644 erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json create mode 100644 erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py create mode 100644 erpnext/regional/doctype/lower_deduction_certificate/test_lower_deduction_certificate.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 9292b633fc3..3cf4d5994a5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -261,12 +261,25 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ price_list: this.frm.doc.buying_price_list }, function() { me.apply_pricing_rule(); - me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0; + me.frm.doc.tax_withholding_category = me.frm.supplier_tds; me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1); + me.frm.set_df_property("tax_withholding_category", "hidden", me.frm.supplier_tds ? 0 : 1); }) }, + apply_tds: function(frm) { + var me = this; + + if (!me.frm.doc.apply_tds) { + me.frm.set_value("tax_withholding_category", ''); + me.frm.set_df_property("tax_withholding_category", "hidden", 1); + } else { + me.frm.set_value("tax_withholding_category", me.frm.supplier_tds); + me.frm.set_df_property("tax_withholding_category", "hidden", 0); + } + }, + credit_to: function() { var me = this; if(this.frm.doc.credit_to) { diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index c7dcf67a4df..a1a20de0503 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -16,6 +16,7 @@ "is_paid", "is_return", "apply_tds", + "tax_withholding_category", "column_break1", "company", "posting_date", @@ -1284,13 +1285,21 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "tax_withholding_category", + "fieldtype": "Link", + "hidden": 1, + "label": "Tax Withholding Category", + "options": "Tax Withholding Category", + "print_hide": 1 } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2020-04-17 13:05:25.199832", + "modified": "2020-04-18 13:05:25.199832", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 3a0c1239927..3654f6ef032 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -959,7 +959,7 @@ class PurchaseInvoice(BuyingController): if not self.apply_tds: return - tax_withholding_details = get_party_tax_withholding_details(self) + tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category) if not tax_withholding_details: return diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 6c31e9efed1..dd6b4fdc603 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -6,23 +6,42 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import flt +from frappe.utils import flt, getdate from erpnext.accounts.utils import get_fiscal_year class TaxWithholdingCategory(Document): pass -def get_party_tax_withholding_details(ref_doc): - tax_withholding_category = frappe.db.get_value('Supplier', ref_doc.supplier, 'tax_withholding_category') +def get_party_tax_withholding_details(ref_doc, tax_withholding_category=None): + + pan_no = '' + suppliers = [] + + if not tax_withholding_category: + tax_withholding_category, pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, ['tax_withholding_category', 'pan']) + if not tax_withholding_category: return + if not pan_no: + pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, 'pan') + + # Get others suppliers with the same PAN No + if pan_no: + suppliers = [d.name for d in frappe.get_all('Supplier', fields=['name'], filters={'pan': pan_no})] + + if not suppliers: + suppliers.append(ref_doc.supplier) + fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company) tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company) if not tax_details: frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}') .format(tax_withholding_category, ref_doc.company)) - tds_amount = get_tds_amount(ref_doc, tax_details, fy) + + tds_amount = get_tds_amount(suppliers, ref_doc.net_total, ref_doc.company, + tax_details, fy, ref_doc.posting_date, pan_no) + tax_row = get_tax_row(tax_details, tds_amount) return tax_row @@ -51,6 +70,7 @@ def get_tax_withholding_rates(tax_withholding, fiscal_year): frappe.throw(_("No Tax Withholding data found for the current Fiscal Year.")) def get_tax_row(tax_details, tds_amount): + return { "category": "Total", "add_deduct_tax": "Deduct", @@ -60,25 +80,36 @@ def get_tax_row(tax_details, tds_amount): "tax_amount": tds_amount } -def get_tds_amount(ref_doc, tax_details, fiscal_year_details): +def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_details, posting_date, pan_no=None): fiscal_year, year_start_date, year_end_date = fiscal_year_details tds_amount = 0 tds_deducted = 0 - def _get_tds(amount): + def _get_tds(amount, rate): if amount <= 0: return 0 - return amount * tax_details.rate / 100 + return amount * rate / 100 + + ldc_name = frappe.db.get_value('Lower Deduction Certificate', + { + 'pan_no': pan_no, + 'fiscal_year': fiscal_year + }, 'name') + ldc = '' + + if ldc_name: + ldc = frappe.get_doc('Lower Deduction Certificate', ldc_name) entries = frappe.db.sql(""" select voucher_no, credit from `tabGL Entry` - where party=%s and fiscal_year=%s and credit > 0 - """, (ref_doc.supplier, fiscal_year), as_dict=1) + where company = %s and + party in %s and fiscal_year=%s and credit > 0 + """, (company, tuple(suppliers), fiscal_year), as_dict=1) vouchers = [d.voucher_no for d in entries] - advance_vouchers = get_advance_vouchers(ref_doc.supplier, fiscal_year) + advance_vouchers = get_advance_vouchers(suppliers, fiscal_year=fiscal_year, company=company) tds_vouchers = vouchers + advance_vouchers @@ -93,7 +124,20 @@ def get_tds_amount(ref_doc, tax_details, fiscal_year_details): tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0 if tds_deducted: - tds_amount = _get_tds(ref_doc.net_total) + if ldc: + limit_consumed = frappe.db.get_value('Purchase Invoice', + { + 'supplier': ('in', suppliers), + 'apply_tds': 1, + 'docstatus': 1 + }, 'sum(net_total)') + + if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, limit_consumed, net_total, + ldc.certificate_limit): + + tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details) + else: + tds_amount = _get_tds(net_total, tax_details.rate) else: supplier_credit_amount = frappe.get_all('Purchase Invoice Item', fields = ['sum(net_amount)'], @@ -106,43 +150,79 @@ def get_tds_amount(ref_doc, tax_details, fiscal_year_details): fields = ['sum(credit_in_account_currency)'], filters = { 'parent': ('in', vouchers), 'docstatus': 1, - 'party': ref_doc.supplier, + 'party': ('in', suppliers), 'reference_type': ('not in', ['Purchase Invoice']) }, as_list=1) supplier_credit_amount += (jv_supplier_credit_amt[0][0] if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0) - supplier_credit_amount += ref_doc.net_total + supplier_credit_amount += net_total - debit_note_amount = get_debit_note_amount(ref_doc.supplier, year_start_date, year_end_date) + debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date) supplier_credit_amount -= debit_note_amount if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold) or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)): - tds_amount = _get_tds(supplier_credit_amount) + + if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total, + ldc.certificate_limit): + tds_amount = get_ltds_amount(supplier_credit_amount, 0, ldc.certificate_limit, ldc.rate, + tax_details) + else: + tds_amount = _get_tds(supplier_credit_amount, tax_details.rate) return tds_amount -def get_advance_vouchers(supplier, fiscal_year=None, company=None, from_date=None, to_date=None): +def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=None, to_date=None): condition = "fiscal_year=%s" % fiscal_year + + if company: + condition += "and company =%s" % (company) if from_date and to_date: - condition = "company=%s and posting_date between %s and %s" % (company, from_date, to_date) + condition += "and posting_date between %s and %s" % (company, from_date, to_date) + + ## Appending the same supplier again if length of suppliers list is 1 + ## since tuple of single element list contains None, For example ('Test Supplier 1', ) + ## and the below query fails + if len(suppliers) == 1: + suppliers.append(suppliers[0]) return frappe.db.sql_list(""" select distinct voucher_no from `tabGL Entry` - where party=%s and %s and debit > 0 - """, (supplier, condition)) or [] + where party in %s and %s and debit > 0 + """, (tuple(suppliers), condition)) or [] -def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None): - condition = "" +def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None): + condition = "and 1=1" if company: condition = " and company=%s " % company + if len(suppliers) == 1: + suppliers.append(suppliers[0]) + return flt(frappe.db.sql(""" select abs(sum(net_total)) from `tabPurchase Invoice` - where supplier=%s %s and is_return=1 and docstatus=1 - and posting_date between %s and %s - """, (supplier, condition, year_start_date, year_end_date))) \ No newline at end of file + where supplier in %s and is_return=1 and docstatus=1 + and posting_date between %s and %s %s + """, (tuple(suppliers), year_start_date, year_end_date, condition))) + +def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details): + if current_amount < (certificate_limit - deducted_amount): + return current_amount * rate/100 + else: + ltds_amount = (certificate_limit - deducted_amount) + tds_amount = current_amount - ltds_amount + + return ltds_amount * rate/100 + tds_amount * tax_details.rate/100 + +def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount, current_amount, certificate_limit): + valid = False + + if ((getdate(valid_from) <= getdate(posting_date) <= getdate(valid_upto)) and + certificate_limit > deducted_amount): + valid = True + + return valid \ No newline at end of file diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py index 2e805f8d3f5..c7cfee74cb0 100644 --- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py +++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py @@ -44,9 +44,14 @@ def get_result(filters): out = [] for supplier in filters.supplier: tds = frappe.get_doc("Tax Withholding Category", supplier.tax_withholding_category) - rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year][0] + rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year] + + if rate: + rate = rate[0] + try: account = [d.account for d in tds.accounts if d.company == filters.company][0] + except IndexError: account = [] total_invoiced_amount, tds_deducted = get_invoice_and_tds_amount(supplier.name, account, @@ -76,7 +81,7 @@ def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date): supplier_credit_amount = flt(sum([d.credit for d in entries])) vouchers = [d.voucher_no for d in entries] - vouchers += get_advance_vouchers(supplier, company=company, + vouchers += get_advance_vouchers([supplier], company=company, from_date=from_date, to_date=to_date) tds_deducted = 0 @@ -89,7 +94,7 @@ def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date): """.format(', '.join(["'%s'" % d for d in vouchers])), (account, from_date, to_date, company))[0][0]) - debit_note_amount = get_debit_note_amount(supplier, from_date, to_date, company=company) + debit_note_amount = get_debit_note_amount([supplier], from_date, to_date, company=company) total_invoiced_amount = supplier_credit_amount + tds_deducted - debit_note_amount diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d8ef786a15f..dd3bc240917 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -651,6 +651,7 @@ erpnext.patches.v12_0.update_price_or_product_discount erpnext.patches.v12_0.add_export_type_field_in_party_master erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22 erpnext.patches.v12_0.create_irs_1099_field_united_states +erpnext.patches.v12_0.add_permission_in_lower_deduction erpnext.patches.v12_0.set_permission_einvoicing erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom erpnext.patches.v12_0.recalculate_requested_qty_in_bin diff --git a/erpnext/patches/v12_0/add_permission_in_lower_deduction.py b/erpnext/patches/v12_0/add_permission_in_lower_deduction.py new file mode 100644 index 00000000000..af9bf74f30e --- /dev/null +++ b/erpnext/patches/v12_0/add_permission_in_lower_deduction.py @@ -0,0 +1,13 @@ +import frappe +from frappe.permissions import add_permission, update_permission_property + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + frappe.reload_doc('regional', 'doctype', 'Lower Deduction Certificate') + + add_permission('Lower Deduction Certificate', 'Accounts Manager', 0) + update_permission_property('Lower Deduction Certificate', 'Accounts Manager', 0, 'write', 1) + update_permission_property('Lower Deduction Certificate', 'Accounts Manager', 0, 'create', 1) \ No newline at end of file diff --git a/erpnext/regional/doctype/lower_deduction_certificate/__init__.py b/erpnext/regional/doctype/lower_deduction_certificate/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.js b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.js new file mode 100644 index 00000000000..8257bf8a969 --- /dev/null +++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Lower Deduction Certificate', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json new file mode 100644 index 00000000000..f48fe6f4763 --- /dev/null +++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json @@ -0,0 +1,138 @@ +{ + "actions": [], + "autoname": "field:certificate_no", + "creation": "2020-03-10 23:12:10.072631", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "certificate_details_section", + "section_code", + "fiscal_year", + "column_break_3", + "certificate_no", + "section_break_3", + "supplier", + "column_break_7", + "pan_no", + "validity_details_section", + "valid_from", + "column_break_10", + "valid_upto", + "section_break_9", + "rate", + "column_break_14", + "certificate_limit" + ], + "fields": [ + { + "fieldname": "certificate_no", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Certificate No", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "section_code", + "fieldtype": "Select", + "label": "Section Code", + "options": "192\n193\n194\n194A\n194C\n194D\n194H\n194I\n194J\n194LA\n194LBB\n194LBC\n195", + "reqd": 1 + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break", + "label": "Deductee Details" + }, + { + "fieldname": "supplier", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Supplier", + "options": "Supplier", + "reqd": 1 + }, + { + "fetch_from": "supplier.pan", + "fetch_if_empty": 1, + "fieldname": "pan_no", + "fieldtype": "Data", + "in_list_view": 1, + "label": "PAN No", + "reqd": 1 + }, + { + "fieldname": "validity_details_section", + "fieldtype": "Section Break", + "label": "Validity Details" + }, + { + "fieldname": "valid_upto", + "fieldtype": "Date", + "label": "Valid Upto", + "reqd": 1 + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break" + }, + { + "fieldname": "rate", + "fieldtype": "Percent", + "label": "Rate Of TDS As Per Certificate", + "reqd": 1 + }, + { + "fieldname": "certificate_limit", + "fieldtype": "Currency", + "label": "Certificate Limit", + "reqd": 1 + }, + { + "fieldname": "certificate_details_section", + "fieldtype": "Section Break", + "label": "Certificate Details" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "valid_from", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Valid From", + "reqd": 1 + }, + { + "fieldname": "fiscal_year", + "fieldtype": "Link", + "label": "Fiscal Year", + "options": "Fiscal Year", + "reqd": 1 + } + ], + "links": [], + "modified": "2020-04-23 23:04:41.203721", + "modified_by": "Administrator", + "module": "Regional", + "name": "Lower Deduction Certificate", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py new file mode 100644 index 00000000000..e8a8ed87505 --- /dev/null +++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import getdate +from frappe.model.document import Document +from erpnext.accounts.utils import get_fiscal_year + +class LowerDeductionCertificate(Document): + def validate(self): + if getdate(self.valid_upto) < getdate(self.valid_from): + frappe.throw(_("Valid Upto date cannot be before Valid From date")) + + fiscal_year = get_fiscal_year(fiscal_year=self.fiscal_year, as_dict=True) + + if not (fiscal_year.year_start_date <= getdate(self.valid_from) \ + <= fiscal_year.year_end_date): + frappe.throw(_("Valid From date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year))) + + if not (fiscal_year.year_start_date <= getdate(self.valid_upto) \ + <= fiscal_year.year_end_date): + frappe.throw(_("Valid Upto date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year))) + diff --git a/erpnext/regional/doctype/lower_deduction_certificate/test_lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/test_lower_deduction_certificate.py new file mode 100644 index 00000000000..7e950206fcc --- /dev/null +++ b/erpnext/regional/doctype/lower_deduction_certificate/test_lower_deduction_certificate.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestLowerDeductionCertificate(unittest.TestCase): + pass diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index da43cc65553..77a466fdff7 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -77,7 +77,7 @@ def add_custom_roles_for_reports(): )).insert() def add_permissions(): - for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report'): + for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate'): add_permission(doctype, 'All', 0) for role in ('Accounts Manager', 'Accounts User', 'System Manager'): add_permission(doctype, role, 0) From 1f3584b9a8162fd432f7ad3832ae1aaf2e3357c3 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Sun, 26 Apr 2020 21:05:09 +0530 Subject: [PATCH 392/929] refactor: employee leave balance report v12 (#21282) * fix: Employee Balance repor fixes * refactor: Employee Leave Balance report For Version-12 Co-authored-by: Nabin Hait --- .../employee_leave_balance.py | 104 +++++++++++++++--- .../employee_leave_balance_summary.py | 54 ++++----- 2 files changed, 112 insertions(+), 46 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index 35c8630e8e2..d20018eef34 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -8,9 +8,6 @@ from frappe.utils import flt from erpnext.hr.doctype.leave_application.leave_application \ import get_leave_balance_on, get_leaves_for_period -from erpnext.hr.report.employee_leave_balance_summary.employee_leave_balance_summary \ - import get_department_leave_approver_map - def execute(filters=None): leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc") @@ -28,6 +25,8 @@ def get_columns(leave_types): for leave_type in leave_types: columns.append(_(leave_type) + " " + _("Opening") + ":Float:160") + columns.append(_(leave_type) + " " + _("Allocated") + ":Float:160") + columns.append(_(leave_type) + " " + _("Expired") + ":Float:160") columns.append(_(leave_type) + " " + _("Taken") + ":Float:160") columns.append(_(leave_type) + " " + _("Balance") + ":Float:160") @@ -68,18 +67,97 @@ def get_data(filters, leave_types): row = [employee.name, employee.employee_name, employee.department] for leave_type in leave_types: - # leaves taken - leaves_taken = get_leaves_for_period(employee.name, leave_type, - filters.from_date, filters.to_date) * -1 - # opening balance - opening = get_leave_balance_on(employee.name, leave_type, filters.from_date) - # closing balance - closing = max(opening - leaves_taken, 0) - - row += [opening, leaves_taken, closing] + row += calculate_leaves_details(filters, leave_type, employee) data.append(row) + return data - return data \ No newline at end of file +def calculate_leaves_details(filters, leave_type, employee): + ledger_entries = get_leave_ledger_entries(filters.from_date, filters.to_date, employee.name, leave_type) + + #Leaves Deducted consist of both expired and leaves taken + leaves_deducted = get_leaves_for_period(employee.name, leave_type, + filters.from_date, filters.to_date) * -1 + + # removing expired leaves + leaves_taken = leaves_deducted - remove_expired_leave(ledger_entries) + + opening = get_leave_balance_on(employee.name, leave_type, filters.from_date) + + new_allocation , expired_allocation = get_allocated_and_expired_leaves(ledger_entries, filters.from_date, filters.to_date) + + #removing leaves taken from expired_allocation + expired_leaves = max(expired_allocation - leaves_taken, 0) + + #Formula for calculating closing balance + closing = max(opening + new_allocation - (leaves_taken + expired_leaves), 0) + + return [opening, new_allocation, expired_leaves, leaves_taken, closing] + + +def remove_expired_leave(records): + expired_within_period = 0 + for record in records: + if record.is_expired: + expired_within_period += record.leaves + return expired_within_period * -1 + + +def get_allocated_and_expired_leaves(records, from_date, to_date): + + from frappe.utils import getdate + + new_allocation = 0 + expired_leaves = 0 + + for record in records: + if record.to_date <= getdate(to_date) and record.leaves>0: + expired_leaves += record.leaves + + if record.from_date >= getdate(from_date) and record.leaves>0: + new_allocation += record.leaves + + return new_allocation, expired_leaves + +def get_leave_ledger_entries(from_date, to_date, employee, leave_type): + records= frappe.db.sql(""" + SELECT + employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type + is_carry_forward, is_expired + FROM `tabLeave Ledger Entry` + WHERE employee=%(employee)s AND leave_type=%(leave_type)s + AND docstatus=1 + AND (from_date between %(from_date)s AND %(to_date)s + OR to_date between %(from_date)s AND %(to_date)s + OR (from_date < %(from_date)s AND to_date > %(to_date)s)) + """, { + "from_date": from_date, + "to_date": to_date, + "employee": employee, + "leave_type": leave_type + }, as_dict=1) + + return records + +def get_department_leave_approver_map(department=None): + conditions='' + if department: + conditions="and (department_name = '%(department)s' or parent_department = '%(department)s')"%{'department': department} + + # get current department and all its child + department_list = frappe.db.sql_list(""" SELECT name FROM `tabDepartment` WHERE disabled=0 {0}""".format(conditions)) #nosec + + # retrieve approvers list from current department and from its subsequent child departments + approver_list = frappe.get_all('Department Approver', filters={ + 'parentfield': 'leave_approvers', + 'parent': ('in', department_list) + }, fields=['parent', 'approver'], as_list=1) + + approvers = {} + + for k, v in approver_list: + approvers.setdefault(k, []).append(v) + + return approvers \ No newline at end of file diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py index 777de022387..53f8f41915e 100644 --- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py +++ b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py @@ -6,6 +6,7 @@ import frappe from frappe.utils import flt from frappe import _ from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on +from erpnext.hr.report.employee_leave_balance.employee_leave_balance import calculate_leaves_details , get_department_leave_approver_map def execute(filters=None): if filters.to_date <= filters.from_date: @@ -38,17 +39,27 @@ def get_columns(): 'label': _('Opening Balance'), 'fieldtype': 'float', 'fieldname': 'opening_balance', - 'width': 160, + 'width': 120, + }, { + 'label': _('New Allocation'), + 'fieldtype': 'Float', + 'fieldname': 'new_allocation', + 'width': 120, + }, { + 'label': _('Expired Leaves'), + 'fieldtype': 'Float', + 'fieldname': 'expired_leaves', + 'width': 120, }, { 'label': _('Leaves Taken'), 'fieldtype': 'float', 'fieldname': 'leaves_taken', - 'width': 160, + 'width': 120, }, { 'label': _('Closing Balance'), 'fieldtype': 'float', 'fieldname': 'closing_balance', - 'width': 160, + 'width': 120, }] return columns @@ -72,7 +83,7 @@ def get_data(filters): 'leave_type': leave_type }) for employee in active_employees: - + leave_approvers = department_approver_map.get(employee.department_name, []).append(employee.leave_approver) if (leave_approvers and len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) \ @@ -82,16 +93,13 @@ def get_data(filters): 'employee_name': employee.employee_name }) - leaves_taken = get_leaves_for_period(employee.name, leave_type, - filters.from_date, filters.to_date) * -1 + leave_details = calculate_leaves_details(filters, leave_type, employee) + row.opening_balance = flt(leave_details[0]) + row.new_allocation = flt(leave_details[1]) + row.expired_leaves = flt(leave_details[2]) + row.leaves_taken = flt(leave_details[3]) + row.closing_balance = flt(leave_details[4]) - opening = get_leave_balance_on(employee.name, leave_type, filters.from_date) - closing = get_leave_balance_on(employee.name, leave_type, filters.to_date) - - row.opening_balance = opening - row.leaves_taken = leaves_taken - row.closing_balance = closing - row.indent = 1 data.append(row) return data @@ -108,23 +116,3 @@ def get_conditions(filters): return conditions -def get_department_leave_approver_map(department=None): - conditions='' - if department: - conditions="and (department_name = '%(department)s' or parent_department = '%(department)s')"%{'department': department} - - # get current department and all its child - department_list = frappe.db.sql_list(""" SELECT name FROM `tabDepartment` WHERE disabled=0 {0}""".format(conditions)) #nosec - - # retrieve approvers list from current department and from its subsequent child departments - approver_list = frappe.get_all('Department Approver', filters={ - 'parentfield': 'leave_approvers', - 'parent': ('in', department_list) - }, fields=['parent', 'approver'], as_list=1) - - approvers = {} - - for k, v in approver_list: - approvers.setdefault(k, []).append(v) - - return approvers From de6f0f7a0591234d0bc9e6e12005b794128816ac Mon Sep 17 00:00:00 2001 From: Himanshu Date: Sun, 26 Apr 2020 23:27:29 +0530 Subject: [PATCH 393/929] fix: add quality inspection template (#21424) --- .../quality_inspection_template_dashboard.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 erpnext/stock/doctype/quality_inspection_template/quality_inspection_template_dashboard.py diff --git a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template_dashboard.py b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template_dashboard.py new file mode 100644 index 00000000000..db459575f3e --- /dev/null +++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template_dashboard.py @@ -0,0 +1,12 @@ +from frappe import _ + +def get_data(): + return { + 'fieldname': 'quality_inspection_template', + 'transactions': [ + { + 'label': _('Quality Inspection'), + 'items': ['Quality Inspection'] + } + ] + } \ No newline at end of file From 2b5bca4ce68c5cefd0df9d96b9112fe8de294028 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sun, 26 Apr 2020 23:28:54 +0530 Subject: [PATCH 394/929] fix: Procurement tracker report (#21422) * fix: procurement report data was not coming * fix: leave allocation minor issue --- .../procurement_tracker.py | 74 +++++++++---------- .../leave_allocation/leave_allocation.py | 6 +- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index 866bf0c7332..39668795cba 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -141,19 +141,18 @@ def get_conditions(filters): conditions = "" if filters.get("company"): - conditions += " AND company=%s"% frappe.db.escape(filters.get('company')) + conditions += " AND par.company=%s" % frappe.db.escape(filters.get('company')) if filters.get("cost_center") or filters.get("project"): conditions += """ - AND (cost_center=%s - OR project=%s) - """% (frappe.db.escape(filters.get('cost_center')), frappe.db.escape(filters.get('project'))) + AND (child.`cost_center`=%s OR child.`project`=%s) + """ % (frappe.db.escape(filters.get('cost_center')), frappe.db.escape(filters.get('project'))) if filters.get("from_date"): - conditions += " AND transaction_date>=%s"% filters.get('from_date') + conditions += " AND par.transaction_date>='%s'" % filters.get('from_date') if filters.get("to_date"): - conditions += " AND transaction_date<=%s"% filters.get('to_date') + conditions += " AND par.transaction_date<='%s'" % filters.get('to_date') return conditions def get_data(filters): @@ -162,7 +161,6 @@ def get_data(filters): mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions) pr_records = get_mapped_pr_records() pi_records = get_mapped_pi_records() - print(pi_records) procurement_record=[] if procurement_record_against_mr: @@ -198,16 +196,16 @@ def get_mapped_mr_details(conditions): mr_records = {} mr_details = frappe.db.sql(""" SELECT - mr.transaction_date, - mr.per_ordered, - mr_item.name, - mr_item.parent, - mr_item.amount - FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item + par.transaction_date, + par.per_ordered, + child.name, + child.parent, + child.amount + FROM `tabMaterial Request` par, `tabMaterial Request Item` child WHERE - mr.per_ordered>=0 - AND mr.name=mr_item.parent - AND mr.docstatus=1 + par.per_ordered>=0 + AND par.name=child.parent + AND par.docstatus=1 {conditions} """.format(conditions=conditions), as_dict=1) #nosec @@ -254,29 +252,29 @@ def get_mapped_pr_records(): def get_po_entries(conditions): return frappe.db.sql(""" SELECT - po_item.name, - po_item.parent, - po_item.cost_center, - po_item.project, - po_item.warehouse, - po_item.material_request, - po_item.material_request_item, - po_item.description, - po_item.stock_uom, - po_item.qty, - po_item.amount, - po_item.base_amount, - po_item.schedule_date, - po.transaction_date, - po.supplier, - po.status, - po.owner - FROM `tabPurchase Order` po, `tabPurchase Order Item` po_item + child.name, + child.parent, + child.cost_center, + child.project, + child.warehouse, + child.material_request, + child.material_request_item, + child.description, + child.stock_uom, + child.qty, + child.amount, + child.base_amount, + child.schedule_date, + par.transaction_date, + par.supplier, + par.status, + par.owner + FROM `tabPurchase Order` par, `tabPurchase Order Item` child WHERE - po.docstatus = 1 - AND po.name = po_item.parent - AND po.status not in ("Closed","Completed","Cancelled") + par.docstatus = 1 + AND par.name = child.parent + AND par.status not in ("Closed","Completed","Cancelled") {conditions} GROUP BY - po.name,po_item.item_code + par.name, child.item_code """.format(conditions=conditions), as_dict=1) #nosec \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index d13bb4577cd..03fe3fa035c 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -30,16 +30,16 @@ class LeaveAllocation(Document): def validate_leave_allocation_days(self): company = frappe.db.get_value("Employee", self.employee, "company") leave_period = get_leave_period(self.from_date, self.to_date, company) - max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") + max_leaves_allowed = flt(frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed")) if max_leaves_allowed > 0: leave_allocated = 0 if leave_period: leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type, leave_period[0].from_date, leave_period[0].to_date) - leave_allocated += self.new_leaves_allocated + leave_allocated += flt(self.new_leaves_allocated) if leave_allocated > max_leaves_allowed: frappe.throw(_("Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period") - .format(self.leave_type, self.employee)) + .format(self.leave_type, self.employee)) def on_submit(self): self.create_leave_ledger_entry() From c2f952045cd83fd54a24a16ad4ebbcee16746b01 Mon Sep 17 00:00:00 2001 From: Anoop Date: Mon, 27 Apr 2020 00:33:26 +0530 Subject: [PATCH 395/929] fix: name 'patient' is not defined possible escape while refactoring, fixed in develop added here. Reported via - https://discuss.erpnext.com/t/healthcare-outpatient-sms-alert-error/60812 --- .../doctype/patient_appointment/patient_appointment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 8f3602af67e..4336fe51606 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -352,7 +352,7 @@ def send_message(doc, message): # jinja to string convertion happens here message = frappe.render_template(message, context) - number = [patient.mobile] + number = [patient_mobile] send_sms(number, message) From 093720b8703177cae771f76511932bff286653b4 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 27 Apr 2020 10:36:11 +0530 Subject: [PATCH 396/929] fix: add hook for sending appointment reminders in v12 (#21432) --- erpnext/hooks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 316c3392851..d387f19692e 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -268,7 +268,8 @@ doc_events = { scheduler_events = { "all": [ - "erpnext.projects.doctype.project.project.project_status_update_reminder" + "erpnext.projects.doctype.project.project.project_status_update_reminder", + "erpnext.healthcare.doctype.patient_appointment.patient_appointment.set_appointment_reminder" ], "hourly": [ 'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails', From 6b082b5edbdc9cbe9fa84910f80a907294d9fe34 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 27 Apr 2020 10:50:03 +0530 Subject: [PATCH 397/929] fix: E-way bill fix in sales invoice --- .../doctype/sales_invoice/regional/india.js | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/regional/india.js b/erpnext/accounts/doctype/sales_invoice/regional/india.js index 7941a59ac36..1ed4b92e7a4 100644 --- a/erpnext/accounts/doctype/sales_invoice/regional/india.js +++ b/erpnext/accounts/doctype/sales_invoice/regional/india.js @@ -25,18 +25,26 @@ frappe.ui.form.on("Sales Invoice", { if(frm.doc.docstatus == 1 && !frm.is_dirty() && !frm.doc.is_return && !frm.doc.ewaybill) { - frm.add_custom_button('e-Way Bill JSON', () => { - var w = window.open( - frappe.urllib.get_full_url( - "/api/method/erpnext.regional.india.utils.generate_ewb_json?" - + "dt=" + encodeURIComponent(frm.doc.doctype) - + "&dn=" + encodeURIComponent(frm.doc.name) - ) - ); - if (!w) { - frappe.msgprint(__("Please enable pop-ups")); return; - } - }, __("Make")); + frm.add_custom_button('E-Way Bill JSON', () => { + frappe.call({ + method: 'erpnext.regional.india.utils.generate_ewb_json', + args: { + 'dt': frm.doc.doctype, + 'dn': [frm.doc.name] + }, + callback: function(r) { + if (r.message) { + const args = { + cmd: 'erpnext.regional.india.utils.download_ewb_json', + data: r.message, + docname: frm.doc.name + }; + open_url_post(frappe.request.url, args); + } + } + }); + + }, __("Create")); } }, From 93e8b5d87ef96be154f325575addac1cb5656996 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 27 Apr 2020 10:50:17 +0530 Subject: [PATCH 398/929] fix: E-way bill fix in List view --- .../sales_invoice/regional/india_list.js | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js index 66d74b4b06a..05c4f1f1dd3 100644 --- a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js +++ b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js @@ -16,17 +16,23 @@ frappe.listview_settings['Sales Invoice'].onload = function (doclist) { } } - var w = window.open( - frappe.urllib.get_full_url( - "/api/method/erpnext.regional.india.utils.generate_ewb_json?" - + "dt=" + encodeURIComponent(doclist.doctype) - + "&dn=" + encodeURIComponent(docnames) - ) - ); - if (!w) { - frappe.msgprint(__("Please enable pop-ups")); return; - } - + frappe.call({ + method: 'erpnext.regional.india.utils.generate_ewb_json', + args: { + 'dt': doclist.doctype, + 'dn': docnames + }, + callback: function(r) { + if (r.message) { + const args = { + cmd: 'erpnext.regional.india.utils.download_ewb_json', + data: r.message, + docname: docnames + }; + open_url_post(frappe.request.url, args); + } + } + }); }; doclist.page.add_actions_menu_item(__('Generate e-Way Bill JSON'), action, false); From 68d7b773145aa3798e06cd22e91054e46fd22eef Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 27 Apr 2020 10:50:40 +0530 Subject: [PATCH 399/929] fix: Utils messsage cleanup --- erpnext/regional/india/utils.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 831985fc7cc..59c6436d906 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -360,8 +360,6 @@ def get_ewb_data(dt, dn): if dt != 'Sales Invoice': frappe.throw(_('e-Way Bill JSON can only be generated from Sales Invoice')) - dn = dn.split(',') - ewaybills = [] for doc_name in dn: doc = frappe.get_doc(dt, doc_name) @@ -439,18 +437,24 @@ def get_ewb_data(dt, dn): @frappe.whitelist() def generate_ewb_json(dt, dn): + dn = json.loads(dn) + return get_ewb_data(dt, dn) - data = get_ewb_data(dt, dn) +@frappe.whitelist() +def download_ewb_json(): + data = frappe._dict(frappe.local.form_dict) - frappe.local.response.filecontent = json.dumps(data, indent=4, sort_keys=True) + frappe.local.response.filecontent = json.dumps(data['data'], indent=4, sort_keys=True) frappe.local.response.type = 'download' - if len(data['billLists']) > 1: + billList = json.loads(data['data'])['billLists'] + + if len(billList) > 1: doc_name = 'Bulk' else: - doc_name = dn + doc_name = data['docname'] - frappe.local.response.filename = '{0}_e-WayBill_Data_{1}.json'.format(doc_name, frappe.utils.random_string(5)) + frappe.local.response.filename = '{0}_e-WayBill_Data_{1}.json'.format(data['docname'], frappe.utils.random_string(5)) @frappe.whitelist() def get_gstins_for_company(company): From 5aa224143986e07062859e9556bf1c8ed3ee18f0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 27 Apr 2020 14:05:45 +0530 Subject: [PATCH 400/929] fix: Test --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- erpnext/regional/india/utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index ced829ee6ee..0b06342f309 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1834,7 +1834,7 @@ class TestSalesInvoice(unittest.TestCase): si.submit() - data = get_ewb_data("Sales Invoice", si.name) + data = get_ewb_data("Sales Invoice", [si.name]) self.assertEqual(data['version'], '1.0.1118') self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR') diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 59c6436d906..e0996c1800d 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -454,7 +454,7 @@ def download_ewb_json(): else: doc_name = data['docname'] - frappe.local.response.filename = '{0}_e-WayBill_Data_{1}.json'.format(data['docname'], frappe.utils.random_string(5)) + frappe.local.response.filename = '{0}_e-WayBill_Data_{1}.json'.format(doc_name, frappe.utils.random_string(5)) @frappe.whitelist() def get_gstins_for_company(company): From 95f7807b78afd6d11fc0237db9955d6cc5900e50 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 Apr 2020 11:15:57 +0530 Subject: [PATCH 401/929] fix: Income tax slab patch (#21448) * fix: reload income_tax_slab_other_charges in patch * fix: reload lower_deduction_certificate in patch --- erpnext/patches/v11_0/add_permissions_in_gst_settings.py | 1 + .../move_tax_slabs_from_payroll_period_to_income_tax_slab.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py index 121a20288c3..d7936110edb 100644 --- a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py +++ b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py @@ -6,4 +6,5 @@ def execute(): if not company: return + frappe.reload_doc("regional", "doctype", "lower_deduction_certificate") add_permissions() \ No newline at end of file diff --git a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py index a6aefac12ad..179be2cfde5 100644 --- a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py +++ b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py @@ -10,7 +10,7 @@ def execute(): if not frappe.db.table_exists("Payroll Period"): return - for doctype in ("income_tax_slab", "salary_structure_assignment", "employee_other_income"): + for doctype in ("income_tax_slab", "salary_structure_assignment", "employee_other_income", "income_tax_slab_other_charges"): frappe.reload_doc("hr", "doctype", doctype) From 558c3284a00d54ad443726ee64cb0313d6cdf284 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 28 Apr 2020 11:17:45 +0530 Subject: [PATCH 402/929] fix: Default column width in Gross profit report --- .../report/gross_profit/gross_profit.py | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 6ef6d6eea03..4e22b05a81d 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -55,27 +55,27 @@ def get_columns(group_wise_columns, filters): columns = [] column_map = frappe._dict({ "parent": _("Sales Invoice") + ":Link/Sales Invoice:120", - "posting_date": _("Posting Date") + ":Date", - "posting_time": _("Posting Time"), - "item_code": _("Item Code") + ":Link/Item", - "item_name": _("Item Name"), - "item_group": _("Item Group") + ":Link/Item Group", - "brand": _("Brand"), - "description": _("Description"), - "warehouse": _("Warehouse") + ":Link/Warehouse", - "qty": _("Qty") + ":Float", - "base_rate": _("Avg. Selling Rate") + ":Currency/currency", - "buying_rate": _("Valuation Rate") + ":Currency/currency", - "base_amount": _("Selling Amount") + ":Currency/currency", - "buying_amount": _("Buying Amount") + ":Currency/currency", - "gross_profit": _("Gross Profit") + ":Currency/currency", - "gross_profit_percent": _("Gross Profit %") + ":Percent", - "project": _("Project") + ":Link/Project", + "posting_date": _("Posting Date") + ":Date:100", + "posting_time": _("Posting Time") + ":Data:100", + "item_code": _("Item Code") + ":Link/Item:100", + "item_name": _("Item Name") + ":Data:100", + "item_group": _("Item Group") + ":Link/Item Group:100", + "brand": _("Brand") + ":Link/Brand:100", + "description": _("Description") +":Data:100", + "warehouse": _("Warehouse") + ":Link/Warehouse:100", + "qty": _("Qty") + ":Float:80", + "base_rate": _("Avg. Selling Rate") + ":Currency/currency:100", + "buying_rate": _("Valuation Rate") + ":Currency/currency:100", + "base_amount": _("Selling Amount") + ":Currency/currency:100", + "buying_amount": _("Buying Amount") + ":Currency/currency:100", + "gross_profit": _("Gross Profit") + ":Currency/currency:100", + "gross_profit_percent": _("Gross Profit %") + ":Percent:100", + "project": _("Project") + ":Link/Project:100", "sales_person": _("Sales person"), - "allocated_amount": _("Allocated Amount") + ":Currency/currency", - "customer": _("Customer") + ":Link/Customer", - "customer_group": _("Customer Group") + ":Link/Customer Group", - "territory": _("Territory") + ":Link/Territory" + "allocated_amount": _("Allocated Amount") + ":Currency/currency:100", + "customer": _("Customer") + ":Link/Customer:100", + "customer_group": _("Customer Group") + ":Link/Customer Group:100", + "territory": _("Territory") + ":Link/Territory:100" }) for col in group_wise_columns.get(scrub(filters.group_by)): @@ -85,7 +85,8 @@ def get_columns(group_wise_columns, filters): "fieldname": "currency", "label" : _("Currency"), "fieldtype": "Link", - "options": "Currency" + "options": "Currency", + "hidden": 1 }) return columns @@ -277,7 +278,7 @@ class GrossProfitGenerator(object): from `tabPurchase Invoice Item` a where a.item_code = %s and a.docstatus=1 and modified <= %s - order by a.modified desc limit 1""", (item_code,self.filters.to_date)) + order by a.modified desc limit 1""", (item_code, self.filters.to_date)) else: last_purchase_rate = frappe.db.sql(""" select (a.base_rate / a.conversion_factor) From fa7d49641363cf6fc8e1ed5625ffd08fa333cd6c Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 28 Apr 2020 13:00:51 +0530 Subject: [PATCH 403/929] fix: Blanket Order in SO/PO child tables (#21444) --- .../doctype/purchase_order/purchase_order.js | 9 --------- erpnext/controllers/queries.py | 13 +++++++++++++ erpnext/public/js/controllers/transaction.js | 14 ++++++++++++++ erpnext/selling/doctype/sales_order/sales_order.js | 9 --------- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 7b1f1354d79..fa4c76f364f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -27,15 +27,6 @@ frappe.ui.form.on("Purchase Order", { frm.set_indicator_formatter('item_code', function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" }) - frm.set_query("blanket_order", "items", function() { - return { - filters: { - "company": frm.doc.company, - "docstatus": 1 - } - } - }); - frm.set_query("expense_account", "items", function() { return { query: "erpnext.controllers.queries.get_expense_account", diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 163ef72ee10..c58dd7a8e36 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -364,6 +364,19 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters): fields = ["name", "parent_account"], limit_start=start, limit_page_length=page_len, as_list=True) +def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters): + return frappe.db.sql("""select distinct bo.name, bo.blanket_order_type, bo.to_date + from `tabBlanket Order` bo, `tabBlanket Order Item` boi + where + boi.parent = bo.name + and boi.item_code = {item_code} + and bo.blanket_order_type = '{blanket_order_type}' + and bo.company = {company} + and bo.docstatus = 1""" + .format(item_code = frappe.db.escape(filters.get("item")), + blanket_order_type = filters.get("blanket_order_type"), + company = frappe.db.escape(filters.get("company")) + )) @frappe.whitelist() def get_income_account(doctype, txt, searchfield, start, page_len, filters): diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 6def5cb51f3..3efb53933e1 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -175,6 +175,20 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }; } + if (this.frm.fields_dict["items"].grid.get_field('blanket_order')) { + this.frm.set_query("blanket_order", "items", function(doc, cdt, cdn) { + var item = locals[cdt][cdn]; + return { + query: "erpnext.controllers.queries.get_blanket_orders", + filters: { + "company": doc.company, + "blanket_order_type": doc.doctype === "Sales Order" ? "Selling" : "Purchasing", + "item": item.item_code + } + } + }); + } + }, onload: function() { var me = this; diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index fa765dfaad9..b4e151b2e30 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -65,15 +65,6 @@ frappe.ui.form.on("Sales Order", { } }); - frm.set_query("blanket_order", "items", function() { - return { - filters: { - "company": frm.doc.company, - "docstatus": 1 - } - } - }); - erpnext.queries.setup_warehouse_query(frm); }, From dd8566ecde94887c44cbac8b8a15d51ecc391d70 Mon Sep 17 00:00:00 2001 From: Anoop Date: Tue, 28 Apr 2020 14:52:10 +0530 Subject: [PATCH 404/929] fix: clinical procedure - set stock entry type clinical procedure - complete and consume - set stock entry type to "Material Issue" report - https://discuss.erpnext.com/t/cant-complete-and-consume/60927 --- .../healthcare/doctype/clinical_procedure/clinical_procedure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index 7c6f4d5999e..a98ae25e43b 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -158,7 +158,7 @@ def get_item_dict(table, parent, parenttype): def create_stock_entry(doc): stock_entry = frappe.new_doc("Stock Entry") stock_entry = set_stock_items(stock_entry, doc.name, "Clinical Procedure") - stock_entry.purpose = "Material Issue" + stock_entry.stock_entry_type = "Material Issue" stock_entry.from_warehouse = doc.warehouse stock_entry.company = doc.company expense_account = get_account(None, "expense_account", "Healthcare Settings", doc.company) From 7c67c38bc555402c33f232d70458a24048a33673 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Tue, 28 Apr 2020 16:08:52 +0530 Subject: [PATCH 405/929] fix: update lead if contact details are changed --- erpnext/crm/utils.py | 24 ++++++++++++++++++++++++ erpnext/hooks.py | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 erpnext/crm/utils.py diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py new file mode 100644 index 00000000000..72d778e9568 --- /dev/null +++ b/erpnext/crm/utils.py @@ -0,0 +1,24 @@ +import frappe + + +def update_lead_phone_numbers(contact, method): + if contact.phone_nos: + contact_lead = contact.get_link_for("Lead") + if contact_lead: + phone = mobile_no = contact.phone_nos[0].phone + + if len(contact.phone_nos) > 1: + # get the default phone number + primary_phones = [phone.phone for phone in contact.phone_nos if phone.is_primary_phone] + if primary_phones: + phone = primary_phones[0] + + # get the default mobile number + primary_mobile_nos = [phone.phone for phone in contact.phone_nos if phone.is_primary_mobile_no] + if primary_mobile_nos: + mobile_no = primary_mobile_nos[0] + + lead = frappe.get_doc("Lead", contact_lead) + lead.phone = phone + lead.mobile_no = mobile_no + lead.save() diff --git a/erpnext/hooks.py b/erpnext/hooks.py index d387f19692e..6712842948e 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -256,7 +256,8 @@ doc_events = { }, "Contact": { "on_trash": "erpnext.support.doctype.issue.issue.update_issue", - "after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information" + "after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information", + "validate": "erpnext.crm.utils.update_lead_phone_numbers" }, "Lead": { "after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information" From c832291bd69b5cbf01afb7c0ddd417eea1717c24 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 28 Apr 2020 19:18:45 +0530 Subject: [PATCH 406/929] fix: Handle empty child table --- erpnext/public/js/controllers/transaction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 215282161d1..7b817f66afa 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1851,8 +1851,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, autofill_warehouse : function (child_table, warehouse_field, warehouse) { - let doctype = child_table[0].doctype; - if (warehouse) { + if (warehouse && child_table && child_table.length) { + let doctype = child_table[0].doctype; $.each(child_table || [], function(i, item) { frappe.model.set_value(doctype, item.name, warehouse_field, warehouse); }); From bd9625d150a6a904138f6805f035bb3df1b6fc83 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 28 Apr 2020 20:19:26 +0530 Subject: [PATCH 407/929] fix: Remove duplicate code from accounting dimension --- .../public/js/utils/dimension_tree_filter.js | 38 +++++++------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index 75c5a820b46..b223fc557be 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -24,7 +24,7 @@ doctypes_with_dimensions.forEach((doctype) => { onload: function(frm) { erpnext.dimension_filters.forEach((dimension) => { frappe.model.with_doctype(dimension['document_type'], () => { - if (frappe.meta.has_field(dimension['document_type'], 'is_group')) { + if(frappe.meta.has_field(dimension['document_type'], 'is_group')) { frm.set_query(dimension['fieldname'], { "is_group": 0 }); @@ -42,19 +42,21 @@ doctypes_with_dimensions.forEach((doctype) => { update_dimension: function(frm) { erpnext.dimension_filters.forEach((dimension) => { - if (frm.is_new()) { - if (frm.doc.company && Object.keys(default_dimensions || {}).length > 0 + if(frm.is_new()) { + if(frm.doc.company && Object.keys(default_dimensions || {}).length > 0 && default_dimensions[frm.doc.company]) { - if (frappe.meta.has_field(doctype, dimension['fieldname'])) { - frm.set_value(dimension['fieldname'], - default_dimensions[frm.doc.company][dimension['document_type']]); - } + let default_dimension = default_dimensions[frm.doc.company][dimension['document_type']]; - $.each(frm.doc.items || frm.doc.accounts || [], function(i, row) { - frappe.model.set_value(row.doctype, row.name, dimension['fieldname'], - default_dimensions[frm.doc.company][dimension['document_type']]) - }); + if(default_dimension) { + if (frappe.meta.has_field(doctype, dimension['fieldname'])) { + frm.set_value(dimension['fieldname'], default_dimension); + } + + $.each(frm.doc.items || frm.doc.accounts || [], function(i, row) { + frappe.model.set_value(row.doctype, row.name, dimension['fieldname'], default_dimension); + }); + } } } }); @@ -71,20 +73,6 @@ child_docs.forEach((doctype) => { }); }, - accounts_add: function(frm, cdt, cdn) { - erpnext.dimension_filters.forEach((dimension) => { - var row = frappe.get_doc(cdt, cdn); - frm.script_manager.copy_from_first_row("accounts", row, [dimension['fieldname']]); - }); - }, - - items_add: function(frm, cdt, cdn) { - erpnext.dimension_filters.forEach((dimension) => { - var row = frappe.get_doc(cdt, cdn); - frm.script_manager.copy_from_first_row("items", row, [dimension['fieldname']]); - }); - }, - accounts_add: function(frm, cdt, cdn) { erpnext.dimension_filters.forEach((dimension) => { var row = frappe.get_doc(cdt, cdn); From 3d2dcd8c599b30909c11b6a99d9ab9795f7d784d Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 29 Apr 2020 02:27:47 +0530 Subject: [PATCH 408/929] fix: payment request not able to make against fees --- erpnext/accounts/doctype/payment_request/payment_request.py | 6 +++--- erpnext/education/doctype/fees/fees.js | 2 ++ erpnext/education/doctype/fees/fees.py | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index f2b0d3db7a1..4e4eac0610f 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -326,7 +326,7 @@ def make_payment_request(**args): "reference_doctype": args.dt, "reference_name": args.dn, "party_type": args.get("party_type") or "Customer", - "party": args.get("party") or ref_doc.customer, + "party": args.get("party") or ref_doc.get("customer"), "bank_account": bank_account }) @@ -420,7 +420,7 @@ def make_payment_entry(docname): def update_payment_req_status(doc, method): from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details - + for ref in doc.references: payment_request_name = frappe.db.get_value("Payment Request", {"reference_doctype": ref.reference_doctype, "reference_name": ref.reference_name, @@ -430,7 +430,7 @@ def update_payment_req_status(doc, method): ref_details = get_reference_details(ref.reference_doctype, ref.reference_name, doc.party_account_currency) pay_req_doc = frappe.get_doc('Payment Request', payment_request_name) status = pay_req_doc.status - + if status != "Paid" and not ref_details.outstanding_amount: status = 'Paid' elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount: diff --git a/erpnext/education/doctype/fees/fees.js b/erpnext/education/doctype/fees/fees.js index e2c6f1d8565..17ef44954b1 100644 --- a/erpnext/education/doctype/fees/fees.js +++ b/erpnext/education/doctype/fees/fees.js @@ -112,6 +112,8 @@ frappe.ui.form.on("Fees", { args: { "dt": frm.doc.doctype, "dn": frm.doc.name, + "party_type": "Student", + "party": frm.doc.student, "recipient_id": frm.doc.student_email }, callback: function(r) { diff --git a/erpnext/education/doctype/fees/fees.py b/erpnext/education/doctype/fees/fees.py index aa616e6206a..f31003bf326 100644 --- a/erpnext/education/doctype/fees/fees.py +++ b/erpnext/education/doctype/fees/fees.py @@ -75,7 +75,8 @@ class Fees(AccountsController): self.make_gl_entries() if self.send_payment_request and self.student_email: - pr = make_payment_request(dt="Fees", dn=self.name, recipient_id=self.student_email, + pr = make_payment_request(party_type="Student", party=self.student, dt="Fees", + dn=self.name, recipient_id=self.student_email, submit_doc=True, use_dummy_message=True) frappe.msgprint(_("Payment request {0} created").format(getlink("Payment Request", pr.name))) From e33c44ae33b617e73e605c9d06c3803dee6c40f0 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 29 Apr 2020 12:13:26 +0530 Subject: [PATCH 409/929] fix: Permission issue Employee Tax exemption (#21492) --- erpnext/hr/doctype/salary_structure/salary_structure.py | 4 ++-- erpnext/regional/india/utils.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py index df76458fe02..5ba7f1c4327 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/salary_structure.py @@ -149,7 +149,7 @@ def get_existing_assignments(employees, salary_structure, from_date): return salary_structures_assignments @frappe.whitelist() -def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0): +def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0, ignore_permissions=False): def postprocess(source, target): if employee: employee_details = frappe.db.get_value("Employee", employee, @@ -169,7 +169,7 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print = "name": "salary_structure" } } - }, target_doc, postprocess, ignore_child_tables=True) + }, target_doc, postprocess, ignore_child_tables=True, ignore_permissions=ignore_permissions) if cint(as_print): doc.name = 'Preview for {0}'.format(employee) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index e0996c1800d..e518c14a8a0 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -273,7 +273,7 @@ def calculate_annual_eligible_hra_exemption(doc): }) def get_component_amt_from_salary_slip(employee, salary_structure, basic_component, hra_component): - salary_slip = make_salary_slip(salary_structure, employee=employee, for_preview=1) + salary_slip = make_salary_slip(salary_structure, employee=employee, for_preview=1, ignore_permissions=True) basic_amt, hra_amt = 0, 0 for earning in salary_slip.earnings: if earning.salary_component == basic_component: From c099a410c88ae7fbef0584448158dfd560fc00d3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 29 Apr 2020 12:05:56 +0530 Subject: [PATCH 410/929] fix: Group by filter fix in item wise sales and purchase register --- .../item_wise_purchase_register/item_wise_purchase_register.py | 2 +- .../report/item_wise_sales_register/item_wise_sales_register.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 127f3133f5b..1f78c7a006f 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -102,7 +102,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum data.append(row) - if filters.get('group_by'): + if filters.get('group_by') and item_list: total_row = total_row_map.get(prev_group_by_value or d.get('item_name')) total_row['percent_gt'] = flt(total_row['total']/grand_total * 100) data.append(total_row) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 0c8957ae441..92a22e62f14 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -111,7 +111,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum data.append(row) - if filters.get('group_by'): + if filters.get('group_by') and item_list: total_row = total_row_map.get(prev_group_by_value or d.get('item_name')) total_row['percent_gt'] = flt(total_row['total']/grand_total * 100) data.append(total_row) From bd969477b1752a95943dae54cf09f706024390bd Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Wed, 29 Apr 2020 13:09:16 +0530 Subject: [PATCH 411/929] fix: AttributeError --- erpnext/crm/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py index 72d778e9568..38bf79e5fcb 100644 --- a/erpnext/crm/utils.py +++ b/erpnext/crm/utils.py @@ -9,12 +9,12 @@ def update_lead_phone_numbers(contact, method): if len(contact.phone_nos) > 1: # get the default phone number - primary_phones = [phone.phone for phone in contact.phone_nos if phone.is_primary_phone] + primary_phones = [phone_doc.phone for phone_doc in contact.phone_nos if phone_doc.is_primary_phone] if primary_phones: phone = primary_phones[0] # get the default mobile number - primary_mobile_nos = [phone.phone for phone in contact.phone_nos if phone.is_primary_mobile_no] + primary_mobile_nos = [phone_doc.phone for phone_doc in contact.phone_nos if phone_doc.is_primary_mobile_no] if primary_mobile_nos: mobile_no = primary_mobile_nos[0] From fd1ab37bc86f1a10b83385f1a2ccd8806a7a4414 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 29 Apr 2020 15:19:08 +0530 Subject: [PATCH 412/929] chore: validate and warn payment against paid invoices --- .../doctype/payment_entry/payment_entry.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index c89742cfb7f..6130a1401fc 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -60,6 +60,7 @@ class PaymentEntry(AccountsController): self.set_remarks() self.validate_duplicate_entry() self.validate_allocated_amount() + self.validate_paid_invoices() self.ensure_supplier_is_not_blocked() self.set_status() @@ -264,6 +265,25 @@ class PaymentEntry(AccountsController): frappe.throw(_("{0} {1} must be submitted") .format(d.reference_doctype, d.reference_name)) + def validate_paid_invoices(self): + no_oustanding_refs = {} + + for d in self.get("references"): + if not d.allocated_amount: + continue + + if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"): + outstanding_amount = frappe.get_cached_value(d.reference_doctype, d.reference_name, "outstanding_amount") + if outstanding_amount <= 0: + no_oustanding_refs.setdefault(d.reference_doctype, []).append(d) + + for k, v in no_oustanding_refs.items(): + frappe.msgprint(_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry. \n \ + If this is undesirable please cancel the corresponding Payment Entry.") + .format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount")), + title=_("Warning"), indicator="orange") + + def validate_journal_entry(self): for d in self.get("references"): if d.allocated_amount and d.reference_doctype == "Journal Entry": From 61584c86011ca87fa05797caa8203d599da9d402 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 30 Apr 2020 10:58:01 +0530 Subject: [PATCH 413/929] fix: print heading field shown in gst section for india region (#21500) Co-authored-by: Nabin Hait --- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index df142a83ddc..ea78c27333e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -149,9 +149,9 @@ "edit_printing_settings", "letter_head", "group_same_items", - "language", - "column_break_84", "select_print_heading", + "column_break_84", + "language", "more_information", "inter_company_invoice_reference", "customer_group", @@ -1570,7 +1570,7 @@ "idx": 181, "is_submittable": 1, "links": [], - "modified": "2020-04-17 12:38:41.435728", + "modified": "2020-04-29 13:37:09.355300", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", From ae5414fced02861c68ba4724b8fbb4a5b50db9c1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 Apr 2020 11:02:45 +0530 Subject: [PATCH 414/929] fix: Consider any kind of exemptions only if tax exemptions are allowed on tax slab (#21475) --- .../income_tax_slab/income_tax_slab.json | 21 ++----- .../salary_component/salary_component.json | 4 +- erpnext/hr/doctype/salary_slip/salary_slip.py | 62 ++++++++++--------- 3 files changed, 41 insertions(+), 46 deletions(-) diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json index 6d89b197d27..bc6bc5ed370 100644 --- a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json +++ b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json @@ -93,13 +93,15 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-24 12:28:36.805904", + "modified": "2020-04-27 20:10:42.755762", "modified_by": "Administrator", "module": "HR", "name": "Income Tax Slab", "owner": "Administrator", "permissions": [ { + "amend": 1, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -109,9 +111,11 @@ "report": 1, "role": "System Manager", "share": 1, + "submit": 1, "write": 1 }, { + "amend": 1, "cancel": 1, "create": 1, "delete": 1, @@ -126,6 +130,7 @@ "write": 1 }, { + "amend": 1, "cancel": 1, "create": 1, "delete": 1, @@ -138,20 +143,6 @@ "share": 1, "submit": 1, "write": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Administrator", - "share": 1, - "submit": 1, - "write": 1 } ], "sort_field": "modified", diff --git a/erpnext/hr/doctype/salary_component/salary_component.json b/erpnext/hr/doctype/salary_component/salary_component.json index 5487e1dee85..97c46c829e7 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.json +++ b/erpnext/hr/doctype/salary_component/salary_component.json @@ -227,7 +227,7 @@ { "default": "0", "depends_on": "eval:doc.type == \"Deduction\" && !doc.variable_based_on_taxable_salary", - "description": "If checked, the full amount will be deducted from taxable income before calculating income tax. Otherwise, it can be exempted via Employee Tax Exemption Declaration.", + "description": "If checked, the full amount will be deducted from taxable income before calculating income tax without any declaration or proof submission.", "fieldname": "exempted_from_income_tax", "fieldtype": "Check", "label": "Exempted from Income Tax" @@ -235,7 +235,7 @@ ], "icon": "fa fa-flag", "links": [], - "modified": "2020-04-24 14:50:28.994054", + "modified": "2020-04-28 15:46:45.252945", "modified_by": "Administrator", "module": "HR", "name": "Salary Component", diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index dda15014166..cfd228fc3d3 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -489,15 +489,16 @@ class SalarySlip(TransactionBase): remaining_sub_periods = get_period_factor(self.employee, self.start_date, self.end_date, self.payroll_frequency, payroll_period)[1] # get taxable_earnings, paid_taxes for previous period - previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date, self.start_date) + previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date, + self.start_date, tax_slab.allow_tax_exemption) previous_total_paid_taxes = self.get_tax_paid_in_period(payroll_period.start_date, self.start_date, tax_component) # get taxable_earnings for current period (all days) - current_taxable_earnings = self.get_taxable_earnings() + current_taxable_earnings = self.get_taxable_earnings(tax_slab.allow_tax_exemption) future_structured_taxable_earnings = current_taxable_earnings.taxable_earnings * (math.ceil(remaining_sub_periods) - 1) # get taxable_earnings, addition_earnings for current actual payment days - current_taxable_earnings_for_payment_days = self.get_taxable_earnings(based_on_payment_days=1) + current_taxable_earnings_for_payment_days = self.get_taxable_earnings(tax_slab.allow_tax_exemption, based_on_payment_days=1) current_structured_taxable_earnings = current_taxable_earnings_for_payment_days.taxable_earnings current_additional_earnings = current_taxable_earnings_for_payment_days.additional_income current_additional_earnings_with_full_tax = current_taxable_earnings_for_payment_days.additional_income_with_full_tax @@ -556,7 +557,7 @@ class SalarySlip(TransactionBase): return income_tax_slab_doc - def get_taxable_earnings_for_prev_period(self, start_date, end_date): + def get_taxable_earnings_for_prev_period(self, start_date, end_date, allow_tax_exemption=False): taxable_earnings = frappe.db.sql(""" select sum(sd.amount) from @@ -576,24 +577,26 @@ class SalarySlip(TransactionBase): }) taxable_earnings = flt(taxable_earnings[0][0]) if taxable_earnings else 0 - exempted_amount = frappe.db.sql(""" - select sum(sd.amount) - from - `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name - where - sd.parentfield='deductions' - and sd.exempted_from_income_tax=1 - and is_flexible_benefit=0 - and ss.docstatus=1 - and ss.employee=%(employee)s - and ss.start_date between %(from_date)s and %(to_date)s - and ss.end_date between %(from_date)s and %(to_date)s - """, { - "employee": self.employee, - "from_date": start_date, - "to_date": end_date - }) - exempted_amount = flt(exempted_amount[0][0]) if exempted_amount else 0 + exempted_amount = 0 + if allow_tax_exemption: + exempted_amount = frappe.db.sql(""" + select sum(sd.amount) + from + `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name + where + sd.parentfield='deductions' + and sd.exempted_from_income_tax=1 + and is_flexible_benefit=0 + and ss.docstatus=1 + and ss.employee=%(employee)s + and ss.start_date between %(from_date)s and %(to_date)s + and ss.end_date between %(from_date)s and %(to_date)s + """, { + "employee": self.employee, + "from_date": start_date, + "to_date": end_date + }) + exempted_amount = flt(exempted_amount[0][0]) if exempted_amount else 0 return taxable_earnings - exempted_amount @@ -621,7 +624,7 @@ class SalarySlip(TransactionBase): return total_tax_paid - def get_taxable_earnings(self, based_on_payment_days=0): + def get_taxable_earnings(self, allow_tax_exemption=False, based_on_payment_days=0): joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee, ["date_of_joining", "relieving_date"]) @@ -655,12 +658,13 @@ class SalarySlip(TransactionBase): else: taxable_earnings += amount - for ded in self.deductions: - if ded.exempted_from_income_tax: - amount = ded.amount - if based_on_payment_days: - amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)[0] - taxable_earnings -= flt(amount) + if allow_tax_exemption: + for ded in self.deductions: + if ded.exempted_from_income_tax: + amount = ded.amount + if based_on_payment_days: + amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)[0] + taxable_earnings -= flt(amount) return frappe._dict({ "taxable_earnings": taxable_earnings, From 22c4f82fc63a6c87357ae0291cba18dd10a61f51 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 Apr 2020 11:03:01 +0530 Subject: [PATCH 415/929] fix: Tax calcualtion based on slab (#21497) --- erpnext/hr/doctype/salary_slip/salary_slip.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index cfd228fc3d3..452aa74281e 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -766,13 +766,13 @@ class SalarySlip(TransactionBase): for slab in tax_slab.slabs: if slab.condition and not self.eval_tax_slab_condition(slab.condition, data): continue - if not slab.to_amount and annual_taxable_earning > slab.from_amount: - tax_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01 + if not slab.to_amount and annual_taxable_earning >= slab.from_amount: + tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction *.01 continue - if annual_taxable_earning > slab.from_amount and annual_taxable_earning < slab.to_amount: - tax_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01 - elif annual_taxable_earning > slab.from_amount and annual_taxable_earning > slab.to_amount: - tax_amount += (slab.to_amount - slab.from_amount) * slab.percent_deduction * .01 + if annual_taxable_earning >= slab.from_amount and annual_taxable_earning < slab.to_amount: + tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction *.01 + elif annual_taxable_earning >= slab.from_amount and annual_taxable_earning >= slab.to_amount: + tax_amount += (slab.to_amount - slab.from_amount + 1) * slab.percent_deduction * .01 # other taxes and charges on income tax for d in tax_slab.other_taxes_and_charges: From 2620ac31f9ef8f7636a2a7a907d4524b949ebe99 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 Apr 2020 11:03:57 +0530 Subject: [PATCH 416/929] fix: Desk links for Income Tax Slab and Employee Other Income (#21511) --- erpnext/config/hr.py | 8 ++++++++ erpnext/hr/doctype/income_tax_slab/income_tax_slab.json | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py index 4e5e9037b3a..1be05a346d0 100644 --- a/erpnext/config/hr.py +++ b/erpnext/config/hr.py @@ -170,6 +170,10 @@ def get_data(): "type": "doctype", "name": "Payroll Period", }, + { + "type": "doctype", + "name": "Income Tax Slab", + }, { "type": "doctype", "name": "Salary Component", @@ -209,6 +213,10 @@ def get_data(): "name": "Employee Tax Exemption Proof Submission", "dependencies": ["Employee"] }, + { + "type": "doctype", + "name": "Employee Other Income", + }, { "type": "doctype", "name": "Employee Benefit Application", diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json index bc6bc5ed370..f74315f32e9 100644 --- a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json +++ b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json @@ -80,6 +80,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "other_taxes_and_charges", "fieldname": "taxes_and_charges_on_income_tax_section", "fieldtype": "Section Break", "label": "Taxes and Charges on Income Tax" @@ -93,7 +94,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-27 20:10:42.755762", + "modified": "2020-04-29 15:08:21.436120", "modified_by": "Administrator", "module": "HR", "name": "Income Tax Slab", From 6397590f0755cea1881b2d562ecc8757cf011961 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 30 Apr 2020 11:04:51 +0530 Subject: [PATCH 417/929] fix: list index out of range error (#21468) * fix: list index out of range error * fix: condition --- .../gross_and_net_profit_report.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py index 260f35f270d..8ce745e4d6b 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -35,6 +35,13 @@ def execute(filters=None): }) return columns, data + + # to avoid error eg: gross_income[0] : list index out of range + if not gross_income: + gross_income = [{}] + if not gross_expense: + gross_expense = [{}] + data.append({ "account_name": "'" + _("Included in Gross Profit") + "'", "account": "'" + _("Included in Gross Profit") + "'" From 0ffff47b64c35b0d493ad6a7d7144ee0dfbcad52 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 30 Apr 2020 11:29:03 +0530 Subject: [PATCH 418/929] feat: force cost center renaming from cost center form (#21504) --- .../doctype/cost_center/cost_center.js | 30 ++++++++---- .../doctype/cost_center/cost_center.json | 3 +- erpnext/accounts/utils.py | 47 +++++++------------ 3 files changed, 39 insertions(+), 41 deletions(-) diff --git a/erpnext/accounts/doctype/cost_center/cost_center.js b/erpnext/accounts/doctype/cost_center/cost_center.js index 96ec57dcb0b..9e2f6eed3b6 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.js +++ b/erpnext/accounts/doctype/cost_center/cost_center.js @@ -18,7 +18,7 @@ frappe.ui.form.on('Cost Center', { }, refresh: function(frm) { if (!frm.is_new()) { - frm.add_custom_button(__('Update Cost Center Number'), function () { + frm.add_custom_button(__('Update Cost Center Name / Number'), function () { frm.trigger("update_cost_center_number"); }); } @@ -47,35 +47,45 @@ frappe.ui.form.on('Cost Center', { }, update_cost_center_number: function(frm) { var d = new frappe.ui.Dialog({ - title: __('Update Cost Center Number'), + title: __('Update Cost Center Name / Number'), fields: [ { - "label": 'Cost Center Number', + "label": "Cost Center Name", + "fieldname": "cost_center_name", + "fieldtype": "Data", + "reqd": 1, + "default": frm.doc.cost_center_name + }, + { + "label": "Cost Center Number", "fieldname": "cost_center_number", "fieldtype": "Data", - "reqd": 1 + "reqd": 1, + "default": frm.doc.cost_center_number } ], primary_action: function() { var data = d.get_values(); - if(data.cost_center_number === frm.doc.cost_center_number) { + if(data.cost_center_name === frm.doc.cost_center_name && data.cost_center_number === frm.doc.cost_center_number) { d.hide(); return; } + frappe.dom.freeze(); frappe.call({ - method: "erpnext.accounts.utils.update_number_field", + method: "erpnext.accounts.utils.update_cost_center", args: { - doctype_name: frm.doc.doctype, - name: frm.doc.name, - field_name: d.fields[0].fieldname, - number_value: data.cost_center_number, + docname: frm.doc.name, + cost_center_name: data.cost_center_name, + cost_center_number: data.cost_center_number, company: frm.doc.company }, callback: function(r) { + frappe.dom.unfreeze(); if(!r.exc) { if(r.message) { frappe.set_route("Form", "Cost Center", r.message); } else { + me.frm.set_value("cost_center_name", data.cost_center_name); me.frm.set_value("cost_center_number", data.cost_center_number); } d.hide(); diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index 126aa543185..5013c92a327 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -2,7 +2,6 @@ "actions": [], "allow_copy": 1, "allow_import": 1, - "allow_rename": 1, "creation": "2013-01-23 19:57:17", "description": "Track separate Income and Expense for product verticals or divisions.", "doctype": "DocType", @@ -126,7 +125,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-03-18 18:26:01.540170", + "modified": "2020-04-29 16:09:30.025214", "modified_by": "Administrator", "module": "Accounts", "name": "Cost Center", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 5ad8cb50d5f..d1aa4011b63 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -817,48 +817,37 @@ def create_payment_gateway_account(gateway): pass @frappe.whitelist() -def update_number_field(doctype_name, name, field_name, number_value, company): +def update_cost_center(docname, cost_center_name, cost_center_number, company): ''' - doctype_name = Name of the DocType - name = Docname being referred - field_name = Name of the field thats holding the 'number' attribute - number_value = Numeric value entered in field_name - - Stores the number entered in the dialog to the DocType's field. - Renames the document by adding the number as a prefix to the current name and updates all transaction where it was present. ''' - doc_title = frappe.db.get_value(doctype_name, name, frappe.scrub(doctype_name)+"_name") + validate_field_number("Cost Center", docname, cost_center_number, company, "cost_center_number") - validate_field_number(doctype_name, name, number_value, company, field_name) + if cost_center_number: + frappe.db.set_value("Cost Center", docname, "cost_center_number", cost_center_number.strip()) + else: + frappe.db.set_value("Cost Center", docname, "cost_center_number", "") - frappe.db.set_value(doctype_name, name, field_name, number_value) + frappe.db.set_value("Cost Center", docname, "cost_center_name", cost_center_name.strip()) - if doc_title[0].isdigit(): - separator = " - " if " - " in doc_title else " " - doc_title = doc_title.split(separator, 1)[1] - - frappe.db.set_value(doctype_name, name, frappe.scrub(doctype_name)+"_name", doc_title) - - new_name = get_autoname_with_number(number_value, doc_title, name, company) - - if name != new_name: - frappe.rename_doc(doctype_name, name, new_name) + new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company) + if docname != new_name: + frappe.rename_doc("Cost Center", docname, new_name, force=1) return new_name -def validate_field_number(doctype_name, name, number_value, company, field_name): +def validate_field_number(doctype_name, docname, number_value, company, field_name): ''' Validate if the number entered isn't already assigned to some other document. ''' if number_value: + filters = {field_name: number_value, "name": ["!=", docname]} if company: - doctype_with_same_number = frappe.db.get_value(doctype_name, - {field_name: number_value, "company": company, "name": ["!=", name]}) - else: - doctype_with_same_number = frappe.db.get_value(doctype_name, - {field_name: number_value, "name": ["!=", name]}) + filters["company"] = company + + doctype_with_same_number = frappe.db.get_value(doctype_name, filters) + if doctype_with_same_number: - frappe.throw(_("{0} Number {1} already used in account {2}") - .format(doctype_name, number_value, doctype_with_same_number)) + frappe.throw(_("{0} Number {1} is already used in {2} {3}") + .format(doctype_name, number_value, doctype_name.lower(), doctype_with_same_number)) def get_autoname_with_number(number_value, doc_title, name, company): ''' append title with prefix as number and suffix as company's abbreviation separated by '-' ''' From 393857d7dec2917092348f9b72a404b5b2218bfe Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 30 Apr 2020 13:23:59 +0530 Subject: [PATCH 419/929] chore: handle credit note validation --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 6130a1401fc..56be17d4405 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -273,12 +273,12 @@ class PaymentEntry(AccountsController): continue if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"): - outstanding_amount = frappe.get_cached_value(d.reference_doctype, d.reference_name, "outstanding_amount") - if outstanding_amount <= 0: + outstanding_amount, is_return = frappe.get_cached_value(d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"]) + if outstanding_amount <= 0 and not is_return: no_oustanding_refs.setdefault(d.reference_doctype, []).append(d) for k, v in no_oustanding_refs.items(): - frappe.msgprint(_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry. \n \ + frappe.msgprint(_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.

\ If this is undesirable please cancel the corresponding Payment Entry.") .format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount")), title=_("Warning"), indicator="orange") From 5e817b2aeed8ab2c8e60d267b82f00a95c675f0e Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 30 Apr 2020 13:56:49 +0530 Subject: [PATCH 420/929] chore: Added company filter and minor cleanup in Stock Balance Report --- .../report/stock_balance/stock_balance.js | 29 +++++++++++---- .../report/stock_balance/stock_balance.json | 36 ++++++++++--------- .../report/stock_balance/stock_balance.py | 7 ++-- 3 files changed, 44 insertions(+), 28 deletions(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.js b/erpnext/stock/report/stock_balance/stock_balance.js index 537fa7c04b8..d77dd877c4a 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.js +++ b/erpnext/stock/report/stock_balance/stock_balance.js @@ -19,6 +19,14 @@ frappe.query_reports["Stock Balance"] = { "reqd": 1, "default": frappe.datetime.get_today() }, + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "width": "80", + "options": "Company", + "default": frappe.defaults.get_default("company") + }, { "fieldname": "item_group", "label": __("Item Group"), @@ -26,12 +34,6 @@ frappe.query_reports["Stock Balance"] = { "width": "80", "options": "Item Group" }, - { - "fieldname":"brand", - "label": __("Brand"), - "fieldtype": "Link", - "options": "Brand" - }, { "fieldname": "item_code", "label": __("Item"), @@ -84,5 +86,18 @@ frappe.query_reports["Stock Balance"] = { "label": __('Show Stock Ageing Data'), "fieldtype": 'Check' }, - ] + ], + + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (column.fieldname == "out_qty" && data && data.out_qty > 0) { + value = "" + value + ""; + } + else if (column.fieldname == "in_qty" && data && data.in_qty > 0) { + value = "" + value + ""; + } + + return value; + } }; diff --git a/erpnext/stock/report/stock_balance/stock_balance.json b/erpnext/stock/report/stock_balance/stock_balance.json index 2f20b202350..8c45f0c2f47 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.json +++ b/erpnext/stock/report/stock_balance/stock_balance.json @@ -1,24 +1,26 @@ { - "add_total_row": 1, - "creation": "2014-10-10 17:58:11.577901", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 2, - "is_standard": "Yes", - "modified": "2018-08-14 15:24:41.395557", - "modified_by": "Administrator", - "module": "Stock", - "name": "Stock Balance", - "owner": "Administrator", - "prepared_report": 1, - "ref_doctype": "Stock Ledger Entry", - "report_name": "Stock Balance", - "report_type": "Script Report", + "add_total_row": 1, + "creation": "2014-10-10 17:58:11.577901", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 2, + "is_standard": "Yes", + "modified": "2020-04-30 13:46:14.680354", + "modified_by": "Administrator", + "module": "Stock", + "name": "Stock Balance", + "owner": "Administrator", + "prepared_report": 1, + "query": "", + "ref_doctype": "Stock Ledger Entry", + "report_name": "Stock Balance", + "report_type": "Script Report", "roles": [ { "role": "Stock User" - }, + }, { "role": "Accounts Manager" } diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index ab87ee114d4..74a4f6ef142 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -94,8 +94,6 @@ def get_columns(filters): {"label": _("Item"), "fieldname": "item_code", "fieldtype": "Link", "options": "Item", "width": 100}, {"label": _("Item Name"), "fieldname": "item_name", "width": 150}, {"label": _("Item Group"), "fieldname": "item_group", "fieldtype": "Link", "options": "Item Group", "width": 100}, - {"label": _("Brand"), "fieldname": "brand", "fieldtype": "Link", "options": "Brand", "width": 90}, - {"label": _("Description"), "fieldname": "description", "width": 140}, {"label": _("Warehouse"), "fieldname": "warehouse", "fieldtype": "Link", "options": "Warehouse", "width": 100}, {"label": _("Stock UOM"), "fieldname": "stock_uom", "fieldtype": "Link", "options": "UOM", "width": 90}, {"label": _("Balance Qty"), "fieldname": "bal_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"}, @@ -132,6 +130,9 @@ def get_conditions(filters): else: frappe.throw(_("'To Date' is required")) + if filters.get("company"): + conditions += " and sle.company = %s" % frappe.db.escape(filters.get("company")) + if filters.get("warehouse"): warehouse_details = frappe.db.get_value("Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1) @@ -233,8 +234,6 @@ def get_items(filters): if filters.get("item_code"): conditions.append("item.name=%(item_code)s") else: - if filters.get("brand"): - conditions.append("item.brand=%(brand)s") if filters.get("item_group"): conditions.append(get_item_group_condition(filters.get("item_group"))) From c31fc19f08af7c6f5e8b7ebcd1f67aedde7cadb0 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 30 Apr 2020 16:28:52 +0530 Subject: [PATCH 421/929] chore: add total row in sales analytics report --- .../sales_analytics/sales_analytics.json | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.json b/erpnext/selling/report/sales_analytics/sales_analytics.json index 71932610a6a..cbc59992483 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.json +++ b/erpnext/selling/report/sales_analytics/sales_analytics.json @@ -1,31 +1,31 @@ { - "add_total_row": 0, - "creation": "2018-09-21 12:46:29.451048", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2019-05-24 05:37:02.866139", - "modified_by": "Administrator", - "module": "Selling", - "name": "Sales Analytics", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Sales Order", - "report_name": "Sales Analytics", - "report_type": "Script Report", + "add_total_row": 1, + "creation": "2018-09-21 12:46:29.451048", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-04-30 16:27:53.112907", + "modified_by": "Administrator", + "module": "Selling", + "name": "Sales Analytics", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Sales Order", + "report_name": "Sales Analytics", + "report_type": "Script Report", "roles": [ { "role": "Stock User" - }, + }, { "role": "Maintenance User" - }, + }, { "role": "Accounts User" - }, + }, { "role": "Sales Manager" } From a8b87ccce0b055e9a36f5270d40dbbc36beb50cf Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 30 Apr 2020 18:39:16 +0530 Subject: [PATCH 422/929] chore: asset accounts should have company currency (#21525) --- .../doctype/asset_category/asset_category.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py index 9bf4df423c2..9a33fc14ac0 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -11,7 +11,8 @@ from frappe.model.document import Document class AssetCategory(Document): def validate(self): self.validate_finance_books() - self.validate_accounts() + self.validate_account_types() + self.validate_account_currency() def validate_finance_books(self): for d in self.finance_books: @@ -19,7 +20,26 @@ class AssetCategory(Document): if cint(d.get(frappe.scrub(field)))<1: frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError) - def validate_accounts(self): + def validate_account_currency(self): + account_types = [ + 'fixed_asset_account', 'accumulated_depreciation_account', 'depreciation_expense_account', 'capital_work_in_progress_account' + ] + invalid_accounts = [] + for d in self.accounts: + company_currency = frappe.get_value('Company', d.get('company_name'), 'default_currency') + for type_of_account in account_types: + if d.get(type_of_account): + account_currency = frappe.get_value("Account", d.get(type_of_account), "account_currency") + if account_currency != company_currency: + invalid_accounts.append(frappe._dict({ 'type': type_of_account, 'idx': d.idx, 'account': d.get(type_of_account) })) + + for d in invalid_accounts: + frappe.throw(_("Row #{}: Currency of {} - {} doesn't matches company currency.") + .format(d.idx, frappe.bold(frappe.unscrub(d.type)), frappe.bold(d.account)), + title=_("Invalid Account")) + + + def validate_account_types(self): account_type_map = { 'fixed_asset_account': { 'account_type': 'Fixed Asset' }, 'accumulated_depreciation_account': { 'account_type': 'Accumulated Depreciation' }, From 426f0bc168222ff9bc87d35fe4b077207d02034f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2020 19:53:59 +0530 Subject: [PATCH 423/929] fix: only check for payment_account on bank entry (#21445) (#21518) * fix: only check for payment_account on bank entry Since all the fields (company, start and end date are mandatory before form submission, there is no need to check for them again after submission. * fix: cur_frm to frm Co-authored-by: Nabin Hait (cherry picked from commit 95b186268cbe8595baf7df36294cb35e8092a4bc) Co-authored-by: Michelle Alva <50285544+michellealva@users.noreply.github.com> --- erpnext/hr/doctype/payroll_entry/payroll_entry.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.js b/erpnext/hr/doctype/payroll_entry/payroll_entry.js index d25eb6d7810..da25d7574e0 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.js @@ -249,7 +249,7 @@ const submit_salary_slip = function (frm) { let make_bank_entry = function (frm) { var doc = frm.doc; - if (doc.company && doc.start_date && doc.end_date && doc.payment_account) { + if (doc.payment_account) { return frappe.call({ doc: cur_frm.doc, method: "make_payment_entry", @@ -262,7 +262,8 @@ let make_bank_entry = function (frm) { freeze_message: __("Creating Payment Entries......") }); } else { - frappe.msgprint(__("Company, Payment Account, From Date and To Date is mandatory")); + frappe.msgprint(__("Payment Account is mandatory")); + frm.scroll_to_field('payment_account'); } }; From 4451b7eda93a9e8ed8362587c07ecaa57f1d72b9 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 30 Apr 2020 20:06:30 +0530 Subject: [PATCH 424/929] chore: calculate total row month-wise in sales analytics --- .../report/sales_analytics/sales_analytics.json | 4 ++-- .../report/sales_analytics/sales_analytics.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.json b/erpnext/selling/report/sales_analytics/sales_analytics.json index cbc59992483..bf9edd6cd49 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.json +++ b/erpnext/selling/report/sales_analytics/sales_analytics.json @@ -1,5 +1,5 @@ { - "add_total_row": 1, + "add_total_row": 0, "creation": "2018-09-21 12:46:29.451048", "disable_prepared_report": 0, "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2020-04-30 16:27:53.112907", + "modified": "2020-04-30 19:49:02.303320", "modified_by": "Administrator", "module": "Selling", "name": "Sales Analytics", diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index f1726ab8bf7..3fc4633ae3d 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -194,6 +194,9 @@ class Analytics(object): def get_rows(self): self.data = [] self.get_periodic_data() + total_row = { + "entity": "Total", + } for entity, period_data in iteritems(self.entity_periodic_data): row = { @@ -207,6 +210,9 @@ class Analytics(object): row[scrub(period)] = amount total += amount + if not total_row.get(scrub(period)): total_row[scrub(period)] = 0 + total_row[scrub(period)] += amount + row["total"] = total if self.filters.tree_type == "Item": @@ -214,9 +220,14 @@ class Analytics(object): self.data.append(row) + self.data.append(total_row) + def get_rows_by_group(self): self.get_periodic_data() out = [] + total_row = { + "entity": "Total", + } for d in reversed(self.group_entries): row = { @@ -232,8 +243,14 @@ class Analytics(object): self.entity_periodic_data.setdefault(d.parent, frappe._dict()).setdefault(period, 0.0) self.entity_periodic_data[d.parent][period] += amount total += amount + + if not total_row.get(scrub(period)): total_row[scrub(period)] = 0 + total_row[scrub(period)] += amount + row["total"] = total out = [row] + out + + out.append(total_row) self.data = out def get_periodic_data(self): From f77a73546908d531d191e56621bccb30e207dd1e Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 1 May 2020 00:01:37 +0530 Subject: [PATCH 425/929] fix: Make Company the first filter --- .../stock/report/stock_balance/stock_balance.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.js b/erpnext/stock/report/stock_balance/stock_balance.js index d77dd877c4a..7d22823eb80 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.js +++ b/erpnext/stock/report/stock_balance/stock_balance.js @@ -3,6 +3,14 @@ frappe.query_reports["Stock Balance"] = { "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "width": "80", + "options": "Company", + "default": frappe.defaults.get_default("company") + }, { "fieldname":"from_date", "label": __("From Date"), @@ -19,14 +27,6 @@ frappe.query_reports["Stock Balance"] = { "reqd": 1, "default": frappe.datetime.get_today() }, - { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "width": "80", - "options": "Company", - "default": frappe.defaults.get_default("company") - }, { "fieldname": "item_group", "label": __("Item Group"), From 438e0f5d4986660bc50663298c0bd8acbfd2b7cb Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 1 May 2020 10:50:02 +0530 Subject: [PATCH 426/929] fix: 'NoneType' object is not iterable (#21538) --- erpnext/stock/get_item_details.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 51f27fd3e78..f43592bef63 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -340,8 +340,14 @@ def get_basic_details(args, item, overwrite_warehouse=True): out["manufacturer_part_no"] = None out["manufacturer"] = None else: - out["manufacturer"], out["manufacturer_part_no"] = frappe.get_value("Item", item.name, - ["default_item_manufacturer", "default_manufacturer_part_no"] ) + data = frappe.get_value("Item", item.name, + ["default_item_manufacturer", "default_manufacturer_part_no"] , as_dict=1) + + if data: + out.update({ + "manufacturer": data.default_item_manufacturer, + "manufacturer_part_no": data.default_manufacturer_part_no + }) child_doctype = args.doctype + ' Item' meta = frappe.get_meta(child_doctype) From db6953dc78b8b31ce5217a241772f92684861115 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 1 May 2020 15:01:04 +0530 Subject: [PATCH 427/929] fix: Party Type filter in payment entry list view (#21542) --- .../doctype/payment_entry/payment_entry_list.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 erpnext/accounts/doctype/payment_entry/payment_entry_list.js diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry_list.js b/erpnext/accounts/doctype/payment_entry/payment_entry_list.js new file mode 100644 index 00000000000..7ea60bb48ed --- /dev/null +++ b/erpnext/accounts/doctype/payment_entry/payment_entry_list.js @@ -0,0 +1,12 @@ +frappe.listview_settings['Payment Entry'] = { + + onload: function(listview) { + listview.page.fields_dict.party_type.get_query = function() { + return { + "filters": { + "name": ["in", Object.keys(frappe.boot.party_account_types)], + } + }; + }; + } +}; \ No newline at end of file From bf1fc47564af4cc5e0d90a933755c9df9ae6a123 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 1 May 2020 18:15:20 +0530 Subject: [PATCH 428/929] fix: accounts payable shows advance amount of other company (#21549) --- erpnext/accounts/party.py | 4 +++- .../accounts_receivable_summary.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 3d9ff273ce2..c8b5240812c 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -603,10 +603,12 @@ def get_party_shipping_address(doctype, name): else: return '' -def get_partywise_advanced_payment_amount(party_type, posting_date = None): +def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None): cond = "1=1" if posting_date: cond = "posting_date <= '{0}'".format(posting_date) + if company: + cond += "and company = '{0}'".format(company) data = frappe.db.sql(""" SELECT party, sum({0}) as amount FROM `tabGL Entry` diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py index b607c0f7028..aa6b42e89d0 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -33,7 +33,7 @@ class AccountsReceivableSummary(ReceivablePayableReport): self.get_party_total(args) party_advance_amount = get_partywise_advanced_payment_amount(self.party_type, - self.filters.report_date) or {} + self.filters.report_date, self.filters.company) or {} for party, party_dict in iteritems(self.party_total): if party_dict.outstanding == 0: From ed709b36b44c8b1e186c15714422bd974fd62325 Mon Sep 17 00:00:00 2001 From: Marica Date: Sat, 2 May 2020 17:55:47 +0530 Subject: [PATCH 429/929] fix: variable referenced before assignment (#21561) --- erpnext/controllers/buying_controller.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index ff07b59bd9b..70f9033f431 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -100,7 +100,7 @@ class BuyingController(StockController): for d in tax_for_valuation: d.category = 'Total' msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items')) - + def validate_asset_return(self): if self.doctype not in ['Purchase Receipt', 'Purchase Invoice'] or not self.is_return: return @@ -663,10 +663,10 @@ class BuyingController(StockController): for qty in range(cint(d.qty)): asset = self.make_asset(d) created_assets.append(asset) - + if len(created_assets) > 5: # dont show asset form links if more than 5 assets are created - messages.append(_('{} Asset{} created for {}').format(len(created_assets), is_plural, frappe.bold(d.item_code))) + messages.append(_('{} Assets created for {}').format(len(created_assets), frappe.bold(d.item_code))) else: assets_link = list(map(lambda d: frappe.utils.get_link_to_form('Asset', d), created_assets)) assets_link = frappe.bold(','.join(assets_link)) From 55b7904e2f3158e6675f21e0f164c2b759c23737 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sat, 2 May 2020 19:39:03 +0530 Subject: [PATCH 430/929] fix: incorrect total in sales analytics for customer/item group --- erpnext/selling/report/sales_analytics/sales_analytics.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 3fc4633ae3d..c0c11339ca8 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -225,9 +225,6 @@ class Analytics(object): def get_rows_by_group(self): self.get_periodic_data() out = [] - total_row = { - "entity": "Total", - } for d in reversed(self.group_entries): row = { @@ -245,12 +242,10 @@ class Analytics(object): total += amount if not total_row.get(scrub(period)): total_row[scrub(period)] = 0 - total_row[scrub(period)] += amount row["total"] = total out = [row] + out - out.append(total_row) self.data = out def get_periodic_data(self): From 272d2bc0b3e9f8e2bf7a9f2f86ac005782b2b25b Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sat, 2 May 2020 19:50:45 +0530 Subject: [PATCH 431/929] fix: review fixes --- erpnext/selling/report/sales_analytics/sales_analytics.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index c0c11339ca8..97d9322918d 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -241,8 +241,6 @@ class Analytics(object): self.entity_periodic_data[d.parent][period] += amount total += amount - if not total_row.get(scrub(period)): total_row[scrub(period)] = 0 - row["total"] = total out = [row] + out From 12b5d72e70690c942e22819bc08eeba5270e299c Mon Sep 17 00:00:00 2001 From: Saqib Date: Sun, 3 May 2020 13:20:30 +0530 Subject: [PATCH 432/929] chore: add validation for gross purchase amount (#21535) * chore: add validation for gross purchase amount * fix: tests --- erpnext/assets/doctype/asset/asset.py | 7 +++++++ erpnext/assets/doctype/asset/test_asset.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 0df6a6c7de0..7c147545372 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -22,6 +22,7 @@ class Asset(AccountsController): self.validate_item() self.set_missing_values() self.prepare_depreciation_data() + self.validate_gross_and_purchase_amount() if self.get("schedules"): self.validate_expected_value_after_useful_life() @@ -123,6 +124,12 @@ class Asset(AccountsController): if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date): frappe.throw(_("Available-for-use Date should be after purchase date")) + + def validate_gross_and_purchase_amount(self): + if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount: + frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\ + Please do not book expense of multiple assets against one single Asset.") + .format(frappe.bold("equal"), "
"), title=_("Invalid Gross Purchase Amount")) def cancel_auto_gen_movement(self): movements = frappe.db.sql( diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index a56440de3d3..b767393532f 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -82,7 +82,6 @@ class TestAsset(unittest.TestCase): doc.set_missing_values() self.assertEquals(doc.items[0].is_fixed_asset, 1) - def test_schedule_for_straight_line_method(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") @@ -599,6 +598,7 @@ def create_asset(**args): "purchase_date": "2015-01-01", "calculate_depreciation": 0, "gross_purchase_amount": 100000, + "purchase_receipt_amount": 100000, "expected_value_after_useful_life": 10000, "warehouse": args.warehouse or "_Test Warehouse - _TC", "available_for_use_date": "2020-06-06", From 0da919c091d1a03943b59bbe9c1fa7e65eee3871 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sun, 3 May 2020 14:53:13 +0530 Subject: [PATCH 433/929] fix: test --- .../report/sales_analytics/test_analytics.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py index 4d81a1e4dda..0a5198ccc53 100644 --- a/erpnext/selling/report/sales_analytics/test_analytics.py +++ b/erpnext/selling/report/sales_analytics/test_analytics.py @@ -83,6 +83,22 @@ class TestAnalytics(unittest.TestCase): "feb_2018": 0.0, "mar_2018": 0.0, "total": 3000.0 + }, + { + "entity": "Total", + "entity_name": "", + "apr_2017": 0.0, + "may_2017": 0.0, + "jun_2017": 2000.0, + "jul_2017": 1000.0, + "aug_2017": 0.0, + "sep_2017": 1500.0, + "oct_2017": 1000.0, + "nov_2017": 0.0, + "dec_2017": 0.0, + "jan_2018": 0.0, + "feb_2018": 0.0, + "mar_2018": 0.0 } ] result = sorted(report[1], key=lambda k: k['entity']) From bdbfd2ad0ca5e91829a2705b4673fcbf37b5804e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 4 May 2020 11:07:10 +0530 Subject: [PATCH 434/929] fix: handle exception if sending Appointment Confirmation message fails (#21568) --- .../patient_appointment/patient_appointment.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 4336fe51606..9bdba032e4a 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -69,12 +69,13 @@ class PatientAppointment(Document): if fee_validity.ref_invoice: frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True) frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till)) - confirm_sms(self) if frappe.db.get_value("Healthcare Settings", None, "manage_appointment_invoice_automatically") == '1' and \ frappe.db.get_value("Patient Appointment", self.name, "invoiced") != 1: invoice_appointment(self) + send_confirmation_msg(self) + @frappe.whitelist() def invoice_appointment(appointment_doc): if not appointment_doc.name: @@ -303,10 +304,14 @@ def set_pending_appointments(): "('Scheduled','Open') and appointment_date < %s", today) -def confirm_sms(doc): - if frappe.db.get_value("Healthcare Settings", None, "app_con") == '1': - message = frappe.db.get_value("Healthcare Settings", None, "app_con_msg") - send_message(doc, message) +def send_confirmation_msg(doc): + if frappe.db.get_single_value("Healthcare Settings", "app_con"): + message = frappe.db.get_single_value("Healthcare Settings", "app_con_msg") + try: + send_message(doc, message) + except Exception: + frappe.log_error(frappe.get_traceback(), _("Appointment Confirmation Message Not Sent")) + frappe.msgprint(_("Appointment Confirmation Message Not Sent"), indicator="orange") @frappe.whitelist() def create_encounter(appointment): From 4a37ee8908f13bda033248873617de76e74c0f0f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 4 May 2020 11:09:19 +0530 Subject: [PATCH 435/929] fix(Healthcare): remove hardcoded UOM during Item creation for templates (#21575) --- .../clinical_procedure_template.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py index 00fad910a4f..69237847986 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py @@ -78,6 +78,8 @@ def create_item_from_template(doc): if doc.is_billable: disabled = 0 + uom = frappe.db.exists('UOM', 'Unit') or frappe.db.get_single_value('Stock Settings', 'stock_uom') + #insert item item = frappe.get_doc({ 'doctype': 'Item', @@ -92,7 +94,7 @@ def create_item_from_template(doc): 'show_in_website': 0, 'is_pro_applicable': 0, 'disabled': disabled, - 'stock_uom': 'Unit' + 'stock_uom': uom }).insert(ignore_permissions=True) #insert item price From 3202b0a486d750412563916b40bf8f49c9682ee0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 4 May 2020 11:12:19 +0530 Subject: [PATCH 436/929] fix: Accounting Dimensions in Period Closing Voucher (#21565) --- .../accounting_dimension.py | 9 ++--- .../period_closing_voucher.py | 33 ++++++++++++++----- .../public/js/utils/dimension_tree_filter.js | 2 +- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 14fdffc0a78..7a85bfb26b6 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -206,12 +206,13 @@ def get_dimension_filters(): WHERE disabled = 0 """, as_dict=1) - default_dimensions = frappe.db.sql("""SELECT parent, company, default_dimension - FROM `tabAccounting Dimension Detail`""", as_dict=1) + default_dimensions = frappe.db.sql("""SELECT p.fieldname, c.company, c.default_dimension + FROM `tabAccounting Dimension Detail` c, `tabAccounting Dimension` p + WHERE c.parent = p.name""", as_dict=1) default_dimensions_map = {} for dimension in default_dimensions: - default_dimensions_map.setdefault(dimension['company'], {}) - default_dimensions_map[dimension['company']][dimension['parent']] = dimension['default_dimension'] + default_dimensions_map.setdefault(dimension.company, {}) + default_dimensions_map[dimension.company][dimension.fieldname] = dimension.default_dimension return dimension_filters, default_dimensions_map diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index eb95e458dc8..b59a177f43b 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -7,6 +7,8 @@ from frappe.utils import flt from frappe import _ from erpnext.accounts.utils import get_account_currency from erpnext.controllers.accounts_controller import AccountsController +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (get_accounting_dimensions, + get_dimension_filters) class PeriodClosingVoucher(AccountsController): def validate(self): @@ -49,7 +51,15 @@ class PeriodClosingVoucher(AccountsController): def make_gl_entries(self): gl_entries = [] net_pl_balance = 0 - pl_accounts = self.get_pl_balances() + dimension_fields = ['t1.cost_center'] + + accounting_dimensions = get_accounting_dimensions() + for dimension in accounting_dimensions: + dimension_fields.append('t1.{0}'.format(dimension)) + + dimension_filters, default_dimensions = get_dimension_filters() + + pl_accounts = self.get_pl_balances(dimension_fields) for acc in pl_accounts: if flt(acc.balance_in_company_currency): @@ -65,34 +75,41 @@ class PeriodClosingVoucher(AccountsController): if flt(acc.balance_in_account_currency) > 0 else 0, "credit": abs(flt(acc.balance_in_company_currency)) \ if flt(acc.balance_in_company_currency) > 0 else 0 - })) + }, item=acc)) net_pl_balance += flt(acc.balance_in_company_currency) if net_pl_balance: cost_center = frappe.db.get_value("Company", self.company, "cost_center") - gl_entries.append(self.get_gl_dict({ + gl_entry = self.get_gl_dict({ "account": self.closing_account_head, "debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0, "debit": abs(net_pl_balance) if net_pl_balance > 0 else 0, "credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0, "credit": abs(net_pl_balance) if net_pl_balance < 0 else 0, "cost_center": cost_center - })) + }) + + for dimension in accounting_dimensions: + gl_entry.update({ + dimension: default_dimensions.get(self.company, {}).get(dimension) + }) + + gl_entries.append(gl_entry) from erpnext.accounts.general_ledger import make_gl_entries make_gl_entries(gl_entries) - def get_pl_balances(self): + def get_pl_balances(self, dimension_fields): """Get balance for pl accounts""" return frappe.db.sql(""" select - t1.account, t1.cost_center, t2.account_currency, + t1.account, t2.account_currency, {dimension_fields}, sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as balance_in_account_currency, sum(t1.debit) - sum(t1.credit) as balance_in_company_currency from `tabGL Entry` t1, `tabAccount` t2 where t1.account = t2.name and t2.report_type = 'Profit and Loss' and t2.docstatus < 2 and t2.company = %s and t1.posting_date between %s and %s - group by t1.account, t1.cost_center - """, (self.company, self.get("year_start_date"), self.posting_date), as_dict=1) + group by t1.account, {dimension_fields} + """.format(dimension_fields = ', '.join(dimension_fields)), (self.company, self.get("year_start_date"), self.posting_date), as_dict=1) diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index b223fc557be..b6720c05cb2 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -46,7 +46,7 @@ doctypes_with_dimensions.forEach((doctype) => { if(frm.doc.company && Object.keys(default_dimensions || {}).length > 0 && default_dimensions[frm.doc.company]) { - let default_dimension = default_dimensions[frm.doc.company][dimension['document_type']]; + let default_dimension = default_dimensions[frm.doc.company][dimension['fieldname']]; if(default_dimension) { if (frappe.meta.has_field(doctype, dimension['fieldname'])) { From c253f0621dd93ccb56cd3330f17db57b8eb4b38c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 4 May 2020 12:40:39 +0530 Subject: [PATCH 437/929] fix: fieldname update for 'Credit' and 'Debit' (#21405) (#21577) * fix: fieldname update for 'Credit' and 'Debit' 'credit' updated to 'credit_in_account_currency' 'debit' updated to 'debit_in_account_currency' * Update journal_entry.py Co-authored-by: Nabin Hait (cherry picked from commit f7a0b8b5b6c92826cef6108a6a25c81d0ac4ae34) Co-authored-by: Andy Zhu --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 1a530c75395..f367f952b8a 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -561,20 +561,20 @@ class JournalEntry(AccountsController): if self.write_off_based_on == 'Accounts Receivable': jd1.party_type = "Customer" - jd1.credit = flt(d.outstanding_amount, self.precision("credit", "accounts")) + jd1.credit_in_account_currency = flt(d.outstanding_amount, self.precision("credit", "accounts")) jd1.reference_type = "Sales Invoice" jd1.reference_name = cstr(d.name) elif self.write_off_based_on == 'Accounts Payable': jd1.party_type = "Supplier" - jd1.debit = flt(d.outstanding_amount, self.precision("debit", "accounts")) + jd1.debit_in_account_currency = flt(d.outstanding_amount, self.precision("debit", "accounts")) jd1.reference_type = "Purchase Invoice" jd1.reference_name = cstr(d.name) jd2 = self.append('accounts', {}) if self.write_off_based_on == 'Accounts Receivable': - jd2.debit = total + jd2.debit_in_account_currency = total elif self.write_off_based_on == 'Accounts Payable': - jd2.credit = total + jd2.credit_in_account_currency = total self.validate_total_debit_and_credit() From c89d750e5cc94aa6c0a13b77ddccd202dc79c9a4 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 4 May 2020 18:53:26 +0530 Subject: [PATCH 438/929] fix: heatmap not working for customer and supplier (#21579) --- erpnext/accounts/party.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index c8b5240812c..b5dfbde5a04 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -163,7 +163,7 @@ def get_default_price_list(party): def set_price_list(party_details, party, party_type, given_price_list, pos=None): # price list price_list = get_permitted_documents('Price List') - + # if there is only one permitted document based on user permissions, set it if price_list and len(price_list) == 1: price_list = price_list[0] @@ -468,23 +468,25 @@ def get_timeline_data(doctype, name): from frappe.desk.form.load import get_communication_data out = {} - fields = 'date(creation), count(name)' + fields = 'creation, count(*)' after = add_years(None, -1).strftime('%Y-%m-%d') - group_by='group by date(creation)' + group_by='group by Date(creation)' - data = get_communication_data(doctype, name, after=after, group_by='group by date(creation)', - fields='date(C.creation) as creation, count(C.name)',as_dict=False) + data = get_communication_data(doctype, name, after=after, group_by='group by creation', + fields='C.creation as creation, count(C.name)',as_dict=False) # fetch and append data from Activity Log data += frappe.db.sql("""select {fields} from `tabActivity Log` - where (reference_doctype="{doctype}" and reference_name="{name}") - or (timeline_doctype in ("{doctype}") and timeline_name="{name}") - or (reference_doctype in ("Quotation", "Opportunity") and timeline_name="{name}") + where (reference_doctype=%(doctype)s and reference_name=%(name)s) + or (timeline_doctype in (%(doctype)s) and timeline_name=%(name)s) + or (reference_doctype in ("Quotation", "Opportunity") and timeline_name=%(name)s) and status!='Success' and creation > {after} {group_by} order by creation desc - """.format(doctype=frappe.db.escape(doctype), name=frappe.db.escape(name), fields=fields, - group_by=group_by, after=after), as_dict=False) + """.format(fields=fields, group_by=group_by, after=after), { + "doctype": doctype, + "name": name + }, as_dict=False) timeline_items = dict(data) From 67ed21d443fedab89d31e1f9d8774e53462b6c64 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Mon, 4 May 2020 19:23:26 +0530 Subject: [PATCH 439/929] fix(patch): Reload GSTR 3B report --- erpnext/patches/v11_0/add_permissions_in_gst_settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py index d7936110edb..83b2a4cc09e 100644 --- a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py +++ b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py @@ -7,4 +7,5 @@ def execute(): return frappe.reload_doc("regional", "doctype", "lower_deduction_certificate") - add_permissions() \ No newline at end of file + frappe.reload_doc("regional", "doctype", "gstr_3b_report") + add_permissions() From c3a9513978fc0b5b5d9d152b708c951a6bb11f70 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 4 May 2020 20:24:00 +0530 Subject: [PATCH 440/929] fix: test --- .../report/sales_analytics/test_analytics.py | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py index 0a5198ccc53..7e8501dcc15 100644 --- a/erpnext/selling/report/sales_analytics/test_analytics.py +++ b/erpnext/selling/report/sales_analytics/test_analytics.py @@ -33,6 +33,21 @@ class TestAnalytics(unittest.TestCase): report = execute(filters) expected_data = [ + { + 'entity': 'Total', + 'apr_2017': 0.0, + 'may_2017': 0.0, + 'jun_2017': 2000.0, + 'jul_2017': 1000.0, + 'aug_2017': 0.0, + 'sep_2017': 1500.0, + 'oct_2017': 1000.0, + 'nov_2017': 0.0, + 'dec_2017': 0.0, + 'jan_2018': 0.0, + 'feb_2018': 2000.0, + 'mar_2018': 0.0 + }, { "entity": "_Test Customer 1", "entity_name": "_Test Customer 1", @@ -83,22 +98,6 @@ class TestAnalytics(unittest.TestCase): "feb_2018": 0.0, "mar_2018": 0.0, "total": 3000.0 - }, - { - "entity": "Total", - "entity_name": "", - "apr_2017": 0.0, - "may_2017": 0.0, - "jun_2017": 2000.0, - "jul_2017": 1000.0, - "aug_2017": 0.0, - "sep_2017": 1500.0, - "oct_2017": 1000.0, - "nov_2017": 0.0, - "dec_2017": 0.0, - "jan_2018": 0.0, - "feb_2018": 0.0, - "mar_2018": 0.0 } ] result = sorted(report[1], key=lambda k: k['entity']) @@ -150,6 +149,21 @@ class TestAnalytics(unittest.TestCase): report = execute(filters) expected_data = [ + { + 'entity': 'Total', + 'apr_2017': 0.0, + 'may_2017': 0.0, + 'jun_2017': 20.0, + 'jul_2017': 10.0, + 'aug_2017': 0.0, + 'sep_2017': 15.0, + 'oct_2017': 10.0, + 'nov_2017': 0.0, + 'dec_2017': 0.0, + 'jan_2018': 0.0, + 'feb_2018': 20.0, + 'mar_2018': 0.0 + }, { "entity": "_Test Customer 1", "entity_name": "_Test Customer 1", From 794bb6ebdd5bc24864eeef370fe9ec425a3025f3 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 5 May 2020 16:12:10 +0530 Subject: [PATCH 441/929] fix: handle make_gl_entry in case of cwip enable after puchasing (#21530) * fix: handle make_gl_entry in case of cwip enable after puchasing * fix: invalid variable assignment * fix: make gl entries if cwip has been booked even if cwip is disabled * add tests * fix: conditions Co-authored-by: Nabin Hait --- erpnext/assets/doctype/asset/asset.py | 53 +++++++++++-- erpnext/assets/doctype/asset/test_asset.py | 75 +++++++++++++++++++ .../doctype/asset_movement/asset_movement.py | 1 + 3 files changed, 121 insertions(+), 8 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 7c147545372..b8daf1fead9 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -32,7 +32,7 @@ class Asset(AccountsController): self.validate_in_use_date() self.set_status() self.make_asset_movement() - if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.asset_category): + if not self.booked_fixed_asset and self.validate_make_gl_entry(): self.make_gl_entries() def before_cancel(self): @@ -454,18 +454,55 @@ class Asset(AccountsController): for d in self.get('finance_books'): if d.finance_book == self.default_finance_book: return cint(d.idx) - 1 + + def validate_make_gl_entry(self): + purchase_document = self.get_purchase_document() + asset_bought_with_invoice = purchase_document == self.purchase_invoice + fixed_asset_account, cwip_account = self.get_asset_accounts() + cwip_enabled = is_cwip_accounting_enabled(self.asset_category) + # check if expense already has been booked in case of cwip was enabled after purchasing asset + expense_booked = False + cwip_booked = False + + if asset_bought_with_invoice: + expense_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""", + (purchase_document, fixed_asset_account), as_dict=1) + else: + cwip_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""", + (purchase_document, cwip_account), as_dict=1) + + if cwip_enabled and (expense_booked or not cwip_booked): + # if expense has already booked from invoice or cwip is booked from receipt + return False + elif not cwip_enabled and (not expense_booked or cwip_booked): + # if cwip is disabled but expense hasn't been booked yet + return True + elif cwip_enabled: + # default condition + return True + + def get_purchase_document(self): + asset_bought_with_invoice = self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock') + purchase_document = self.purchase_invoice if asset_bought_with_invoice else self.purchase_receipt + + return purchase_document + + def get_asset_accounts(self): + fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name, + asset_category = self.asset_category, company = self.company) + + cwip_account = get_asset_account("capital_work_in_progress_account", + self.name, self.asset_category, self.company) + + return fixed_asset_account, cwip_account def make_gl_entries(self): gl_entries = [] - if ((self.purchase_receipt \ - or (self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock'))) - and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()): - fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name, - asset_category = self.asset_category, company = self.company) + purchase_document = self.get_purchase_document() + fixed_asset_account, cwip_account = self.get_asset_accounts() - cwip_account = get_asset_account("capital_work_in_progress_account", - self.name, self.asset_category, self.company) + if (purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()): gl_entries.append(self.get_gl_dict({ "account": cwip_account, diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index b767393532f..e8def53c070 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -563,6 +563,81 @@ class TestAsset(unittest.TestCase): self.assertEqual(gle, expected_gle) + def test_gle_with_cwip_toggling(self): + # TEST: purchase an asset with cwip enabled and then disable cwip and try submitting the asset + frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1) + + pr = make_purchase_receipt(item_code="Macbook Pro", + qty=1, rate=5000, do_not_submit=True, location="Test Location") + pr.set('taxes', [{ + 'category': 'Total', + 'add_deduct_tax': 'Add', + 'charge_type': 'On Net Total', + 'account_head': '_Test Account Service Tax - _TC', + 'description': '_Test Account Service Tax', + 'cost_center': 'Main - _TC', + 'rate': 5.0 + }, { + 'category': 'Valuation and Total', + 'add_deduct_tax': 'Add', + 'charge_type': 'On Net Total', + 'account_head': '_Test Account Shipping Charges - _TC', + 'description': '_Test Account Shipping Charges', + 'cost_center': 'Main - _TC', + 'rate': 5.0 + }]) + pr.submit() + expected_gle = ( + ("Asset Received But Not Billed - _TC", 0.0, 5250.0), + ("CWIP Account - _TC", 5250.0, 0.0) + ) + pr_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` + where voucher_type='Purchase Receipt' and voucher_no = %s + order by account""", pr.name) + self.assertEqual(pr_gle, expected_gle) + + pi = make_invoice(pr.name) + pi.submit() + expected_gle = ( + ("_Test Account Service Tax - _TC", 250.0, 0.0), + ("_Test Account Shipping Charges - _TC", 250.0, 0.0), + ("Asset Received But Not Billed - _TC", 5250.0, 0.0), + ("Creditors - _TC", 0.0, 5500.0), + ("Expenses Included In Asset Valuation - _TC", 0.0, 250.0), + ) + pi_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` + where voucher_type='Purchase Invoice' and voucher_no = %s + order by account""", pi.name) + self.assertEqual(pi_gle, expected_gle) + + asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name') + asset_doc = frappe.get_doc('Asset', asset) + month_end_date = get_last_day(nowdate()) + asset_doc.available_for_use_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15) + self.assertEqual(asset_doc.gross_purchase_amount, 5250.0) + asset_doc.append("finance_books", { + "expected_value_after_useful_life": 200, + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 10, + "depreciation_start_date": month_end_date + }) + + # disable cwip and try submitting + frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0) + asset_doc.submit() + # asset should have gl entries even if cwip is disabled + expected_gle = ( + ("_Test Fixed Asset - _TC", 5250.0, 0.0), + ("CWIP Account - _TC", 0.0, 5250.0) + ) + gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` + where voucher_type='Asset' and voucher_no = %s + order by account""", asset_doc.name) + self.assertEqual(gle, expected_gle) + + frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1) + def test_expense_head(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=2, rate=200000.0, location="Test Location") diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py index 3a08baa714e..3da355e2b99 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/asset_movement.py @@ -110,6 +110,7 @@ class AssetMovement(Document): ORDER BY asm.transaction_date asc """, (d.asset, self.company, 'Receipt'), as_dict=1) + if auto_gen_movement_entry and auto_gen_movement_entry[0].get('name') == self.name: frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \ auto generated for Asset {1}').format(self.name, d.asset)) From 10ea82001ff1ed23c26da7c0ed4a12c962f06106 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 5 May 2020 16:14:12 +0530 Subject: [PATCH 442/929] chore: fix error message (#21594) * chore: fix error message * chore: add row idx --- erpnext/controllers/selling_controller.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index de771a55f2c..c25ad060674 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -165,9 +165,9 @@ class SellingController(StockController): d.stock_qty = flt(d.qty) * flt(d.conversion_factor) def validate_selling_price(self): - def throw_message(item_name, rate, ref_rate_field): - frappe.throw(_("""Selling rate for item {0} is lower than its {1}. Selling rate should be atleast {2}""") - .format(item_name, ref_rate_field, rate)) + def throw_message(idx, item_name, rate, ref_rate_field): + frappe.throw(_("""Row #{}: Selling rate for item {} is lower than its {}. Selling rate should be atleast {}""") + .format(idx, item_name, ref_rate_field, rate)) if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"): return @@ -182,7 +182,7 @@ class SellingController(StockController): last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"]) last_purchase_rate_in_sales_uom = last_purchase_rate / (it.conversion_factor or 1) if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom): - throw_message(it.item_name, last_purchase_rate_in_sales_uom, "last purchase rate") + throw_message(it.idx, frappe.bold(it.item_name), last_purchase_rate_in_sales_uom, "last purchase rate") last_valuation_rate = frappe.db.sql(""" SELECT valuation_rate FROM `tabStock Ledger Entry` WHERE item_code = %s @@ -192,7 +192,7 @@ class SellingController(StockController): if last_valuation_rate: last_valuation_rate_in_sales_uom = last_valuation_rate[0][0] / (it.conversion_factor or 1) if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate_in_sales_uom): - throw_message(it.name, last_valuation_rate_in_sales_uom, "valuation rate") + throw_message(it.idx, frappe.bold(it.item_name), last_valuation_rate_in_sales_uom, "valuation rate") def get_item_list(self): From d476eb79b4427f9e981953f3011c6ba8eacf1e1c Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 5 May 2020 16:17:03 +0530 Subject: [PATCH 443/929] fix: against voucher no not all records showing in case of Group By Voucher (consolidated) (#21591) --- erpnext/accounts/report/general_ledger/general_ledger.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index d3f3882299b..ae407231caa 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -293,6 +293,9 @@ def get_accountwise_gle(filters, gl_entries, gle_map): data[key].debit_in_account_currency += flt(gle.debit_in_account_currency) data[key].credit_in_account_currency += flt(gle.credit_in_account_currency) + if data[key].against_voucher: + data[key].against_voucher += ', ' + gle.against_voucher + from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) for gle in gl_entries: if (gle.posting_date < from_date or From c6e0dbb1c4eb4251c65ec955fa3d7dc31d97c247 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Tue, 5 May 2020 16:26:05 +0530 Subject: [PATCH 444/929] Appending Email and Phone in Child Table --- erpnext/selling/doctype/customer/customer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 493d8730602..3da0eb86d1d 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -164,6 +164,8 @@ class Customer(TransactionBase): contact.phone = lead.phone contact.mobile_no = lead.mobile_no contact.is_primary_contact = 1 + contact.append('email_ids', dict(email_id=lead.email_id, is_primary=1)) + contact.append('phone_nos', dict(phone=lead.mobile_no, is_primary_mobile_no=1)) contact.append('links', dict(link_doctype='Customer', link_name=self.name)) contact.flags.ignore_permissions = self.flags.ignore_permissions contact.autoname() From b4311a41c51efe23b64afd548fda7d68d3ac9acf Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 5 May 2020 16:15:07 +0530 Subject: [PATCH 445/929] fix: work order operation completed qty --- erpnext/manufacturing/doctype/job_card/job_card.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 4c1ab7479fb..8fce3161375 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -107,30 +107,31 @@ class JobCard(Document): for_quantity, time_in_mins = 0, 0 from_time_list, to_time_list = [], [] - + field = "operation_id" if self.operation_id else "operation" data = frappe.get_all('Job Card', fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"], filters = {"docstatus": 1, "work_order": self.work_order, - "workstation": self.workstation, "operation": self.operation}) + "workstation": self.workstation, field: self.get(field)}) if data and len(data) > 0: for_quantity = data[0].completed_qty time_in_mins = data[0].time_in_mins - if for_quantity: + if self.get(field): time_data = frappe.db.sql(""" SELECT min(from_time) as start_time, max(to_time) as end_time FROM `tabJob Card` jc, `tabJob Card Time Log` jctl WHERE jctl.parent = jc.name and jc.work_order = %s - and jc.workstation = %s and jc.operation = %s and jc.docstatus = 1 - """, (self.work_order, self.workstation, self.operation), as_dict=1) + and jc.workstation = %s and jc.{0} = %s and jc.docstatus = 1 + """.format(field), (self.work_order, self.workstation, self.get(field)), as_dict=1) wo = frappe.get_doc('Work Order', self.work_order) + work_order_field = "name" if field == "operation_id" else field for data in wo.operations: - if data.workstation == self.workstation and data.operation == self.operation: + if data.get(work_order_field) == self.get(field) and data.workstation == self.workstation: data.completed_qty = for_quantity data.actual_operation_time = time_in_mins data.actual_start_time = time_data[0].start_time if time_data else None From 178ad9b4d614239d9880bf0b16291afa82aa5d58 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Tue, 5 May 2020 20:07:48 +0530 Subject: [PATCH 446/929] fix(patch): reload Expense Claim doctype --- .../patches/v12_0/set_correct_status_for_expense_claim.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py b/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py index 1fefc90680e..c3a87fc5ca7 100644 --- a/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py +++ b/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py @@ -4,8 +4,11 @@ import frappe def execute(): + + frappe.reload_doc("hr", "doctype", "expense_claim") + frappe.db.sql(""" update `tabExpense Claim` set status = 'Paid' where total_advance_amount + total_amount_reimbursed = total_sanctioned_amount + total_taxes_and_charges - """) \ No newline at end of file + """) From ec3a462acdafca453c26d8f90d5c1839a863ba62 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 7 May 2020 12:06:05 +0530 Subject: [PATCH 447/929] fix: list index out of range (#21614) --- erpnext/stock/doctype/batch/batch.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 9b7249e66b9..a091ac7fae9 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -7,7 +7,7 @@ import frappe from frappe import _ from frappe.model.document import Document from frappe.model.naming import make_autoname, revert_series_if_last -from frappe.utils import flt, cint +from frappe.utils import flt, cint, get_link_to_form from frappe.utils.jinja import render_template from frappe.utils.data import add_days from six import string_types @@ -124,7 +124,7 @@ class Batch(Document): if has_expiry_date and not self.expiry_date: frappe.throw(msg=_("Please set {0} for Batched Item {1}, which is used to set {2} on Submit.") \ .format(frappe.bold("Shelf Life in Days"), - frappe.utils.get_link_to_form("Item", self.item), + get_link_to_form("Item", self.item), frappe.bold("Batch Expiry Date")), title=_("Expiry Date Mandatory")) @@ -264,16 +264,20 @@ def get_batch_no(item_code, warehouse, qty=1, throw=False, serial_no=None): def get_batches(item_code, warehouse, qty=1, throw=False, serial_no=None): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos cond = '' - if serial_no: + if serial_no and frappe.get_cached_value('Item', item_code, 'has_batch_no'): + serial_nos = get_serial_nos(serial_no) batch = frappe.get_all("Serial No", fields = ["distinct batch_no"], filters= { "item_code": item_code, "warehouse": warehouse, - "name": ("in", get_serial_nos(serial_no)) + "name": ("in", serial_nos) } ) + if not batch: + validate_serial_no_with_batch(serial_nos, item_code) + if batch and len(batch) > 1: return [] @@ -288,4 +292,15 @@ def get_batches(item_code, warehouse, qty=1, throw=False, serial_no=None): and (`tabBatch`.expiry_date >= CURDATE() or `tabBatch`.expiry_date IS NULL) {0} group by batch_id order by `tabBatch`.expiry_date ASC, `tabBatch`.creation ASC - """.format(cond), (item_code, warehouse), as_dict=True) \ No newline at end of file + """.format(cond), (item_code, warehouse), as_dict=True) + +def validate_serial_no_with_batch(serial_nos, item_code): + if frappe.get_cached_value("Serial No", serial_nos[0], "item_code") != item_code: + frappe.throw(_("The serial no {0} does not belong to item {1}") + .format(get_link_to_form("Serial No", serial_nos[0]), get_link_to_form("Item", item_code))) + + serial_no_link = ','.join([get_link_to_form("Serial No", sn) for sn in serial_nos]) + + message = "Serial Nos" if len(serial_nos) > 1 else "Serial No" + frappe.throw(_("There is no batch found against the {0}: {1}") + .format(message, serial_no_link)) \ No newline at end of file From 2aa045865fc85352d912d28c091ec46fb79454cd Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 7 May 2020 12:11:23 +0530 Subject: [PATCH 448/929] fix(item): patch to rename duplicate item_code values to name (#21619) --- erpnext/patches.txt | 1 + .../patches/v11_0/rename_duplicate_item_code_values.py | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 erpnext/patches/v11_0/rename_duplicate_item_code_values.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index dd3bc240917..580126d401e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -496,6 +496,7 @@ erpnext.patches.v10_0.rename_offer_letter_to_job_offer execute:frappe.delete_doc('DocType', 'Production Planning Tool', ignore_missing=True) erpnext.patches.v10_0.migrate_daily_work_summary_settings_to_daily_work_summary_group # 24-12-2018 erpnext.patches.v10_0.add_default_cash_flow_mappers +erpnext.patches.v11_0.rename_duplicate_item_code_values erpnext.patches.v11_0.make_quality_inspection_template erpnext.patches.v10_0.update_status_for_multiple_source_in_po erpnext.patches.v10_0.set_auto_created_serial_no_in_stock_entry diff --git a/erpnext/patches/v11_0/rename_duplicate_item_code_values.py b/erpnext/patches/v11_0/rename_duplicate_item_code_values.py new file mode 100644 index 00000000000..00ab562c353 --- /dev/null +++ b/erpnext/patches/v11_0/rename_duplicate_item_code_values.py @@ -0,0 +1,8 @@ +import frappe + +def execute(): + items = [] + items = frappe.db.sql("""select item_code from `tabItem` group by item_code having count(*) > 1""", as_dict=True) + if items: + for item in items: + frappe.db.sql("""update `tabItem` set item_code=name where item_code = %s""", (item.item_code)) From 9bd6d119c405f2ace6c93d53647af140c6543542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrker=20Tunal=C4=B1?= Date: Thu, 7 May 2020 12:38:37 +0300 Subject: [PATCH 449/929] fix: Job Card submitted qty Update Operation Status function in work order was throwing exception without checking the "Overproduction Percentage For Work Order" setting. To submit Job Card qty for more than the Work Order's "To Manufacture Qty" we need to apply this fix. --- erpnext/manufacturing/doctype/work_order/work_order.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 8a39f7420e9..a7e1acc524c 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -382,6 +382,9 @@ class WorkOrder(Document): return holidays[holiday_list] def update_operation_status(self): + allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", "overproduction_percentage_for_work_order")) + max_allowed_qty_for_wo = flt(self.qty) + (allowance_percentage/100 * flt(self.qty)) + for d in self.get("operations"): if not d.completed_qty: d.status = "Pending" @@ -389,6 +392,8 @@ class WorkOrder(Document): d.status = "Work in Progress" elif flt(d.completed_qty) == flt(self.qty): d.status = "Completed" + elif flt(d.completed_qty) <= max_allowed_qty_for_wo: + d.status = "Completed" else: frappe.throw(_("Completed Qty can not be greater than 'Qty to Manufacture'")) @@ -809,4 +814,4 @@ def create_pick_list(source_name, target_doc=None, for_qty=None): doc.set_item_locations() - return doc \ No newline at end of file + return doc From cd0fcfd84cc5015fad41c6c96412317cc8ffeef0 Mon Sep 17 00:00:00 2001 From: Myuddin khatri Date: Thu, 7 May 2020 15:11:39 +0530 Subject: [PATCH 450/929] solved merge conflicts --- erpnext/crm/utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py index 38bf79e5fcb..95b19ec21ec 100644 --- a/erpnext/crm/utils.py +++ b/erpnext/crm/utils.py @@ -19,6 +19,5 @@ def update_lead_phone_numbers(contact, method): mobile_no = primary_mobile_nos[0] lead = frappe.get_doc("Lead", contact_lead) - lead.phone = phone - lead.mobile_no = mobile_no - lead.save() + lead.db_set("phone", phone) + lead.db_set("mobile_no", mobile_no) From 2d430ec07779be52b78bbc6b6f2fa30bfd37dc16 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 7 May 2020 18:57:01 +0530 Subject: [PATCH 451/929] feat: Income tax slab (#21631) * feat: Income tax slab (#21406) * Feat: Multiple tax as per new taxation rule * patch:for multiple tax slab, fix: payroll and exemption validation * Test: Fixture * feat: income tax slab with other charges and tax exempted deduction components * fix: added missing init file * fix: Patch fixed * fix: Patch fixed * fix: test fixes * fix: validate duplicate exemption declaration * fix: payment entry test case Co-authored-by: Anurag Mishra * fix: Income tax slab patch (#21448) * fix: reload income_tax_slab_other_charges in patch * fix: reload lower_deduction_certificate in patch * fix: Consider any kind of exemptions only if tax exemptions are allowed on tax slab (#21475) * fix: Tax calcualtion based on slab (#21497) * fix: Desk links for Income Tax Slab and Employee Other Income (#21511) * fix: patch Co-authored-by: Anurag Mishra --- .../doctype/sales_invoice/sales_invoice.js | 10 +- erpnext/config/hr.py | 8 + .../doctype/employee_other_income/__init__.py | 0 .../employee_other_income.js | 8 + .../employee_other_income.json | 138 +++ .../employee_other_income.py | 10 + .../test_employee_other_income.py | 10 + .../employee_tax_exemption_declaration.json | 746 +++----------- .../employee_tax_exemption_declaration.py | 20 +- ...test_employee_tax_exemption_declaration.py | 2 +- ...ployee_tax_exemption_proof_submission.json | 731 +++----------- ...employee_tax_exemption_proof_submission.py | 6 +- .../hr/doctype/income_tax_slab/__init__.py | 0 .../income_tax_slab/income_tax_slab.js | 6 + .../income_tax_slab/income_tax_slab.json | 152 +++ .../income_tax_slab/income_tax_slab.py | 10 + .../income_tax_slab/test_income_tax_slab.py | 10 + .../income_tax_slab_other_charges/__init__.py | 0 .../income_tax_slab_other_charges.json | 75 ++ .../income_tax_slab_other_charges.py | 10 + .../payroll_period/payroll_period.json | 467 ++------- .../doctype/payroll_period/payroll_period.py | 5 +- .../salary_component/salary_component.json | 525 +++++----- .../salary_component/test_records.json | 4 - .../salary_component/test_salary_component.py | 1 - .../doctype/salary_detail/salary_detail.json | 931 ++++-------------- erpnext/hr/doctype/salary_slip/salary_slip.py | 162 ++- .../doctype/salary_slip/test_salary_slip.py | 76 +- .../salary_structure/salary_structure.js | 1 + .../salary_structure/salary_structure.py | 23 +- .../salary_structure/test_salary_structure.py | 20 +- .../salary_structure_assignment.js | 10 + .../salary_structure_assignment.json | 13 +- erpnext/hr/utils.py | 13 + erpnext/patches.txt | 1 + .../v11_0/add_permissions_in_gst_settings.py | 1 + .../v11_0/set_salary_component_properties.py | 3 +- erpnext/patches/v13_0/__init__.py | 0 ..._from_payroll_period_to_income_tax_slab.py | 99 ++ erpnext/regional/india/setup.py | 18 +- 40 files changed, 1604 insertions(+), 2721 deletions(-) create mode 100644 erpnext/hr/doctype/employee_other_income/__init__.py create mode 100644 erpnext/hr/doctype/employee_other_income/employee_other_income.js create mode 100644 erpnext/hr/doctype/employee_other_income/employee_other_income.json create mode 100644 erpnext/hr/doctype/employee_other_income/employee_other_income.py create mode 100644 erpnext/hr/doctype/employee_other_income/test_employee_other_income.py create mode 100644 erpnext/hr/doctype/income_tax_slab/__init__.py create mode 100644 erpnext/hr/doctype/income_tax_slab/income_tax_slab.js create mode 100644 erpnext/hr/doctype/income_tax_slab/income_tax_slab.json create mode 100644 erpnext/hr/doctype/income_tax_slab/income_tax_slab.py create mode 100644 erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py create mode 100644 erpnext/hr/doctype/income_tax_slab_other_charges/__init__.py create mode 100644 erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json create mode 100644 erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py create mode 100644 erpnext/patches/v13_0/__init__.py create mode 100644 erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 7a7d25aadcc..d7e5114042d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -586,7 +586,9 @@ frappe.ui.form.on('Sales Invoice', { frm.set_query("account_for_change_amount", function() { return { filters: { - account_type: ['in', ["Cash", "Bank"]] + account_type: ['in', ["Cash", "Bank"]], + company: frm.doc.company, + is_group: 0 } }; }); @@ -667,7 +669,8 @@ frappe.ui.form.on('Sales Invoice', { frm.fields_dict["loyalty_redemption_account"].get_query = function() { return { filters:{ - "company": frm.doc.company + "company": frm.doc.company, + "is_group": 0 } } }; @@ -676,7 +679,8 @@ frappe.ui.form.on('Sales Invoice', { frm.fields_dict["loyalty_redemption_cost_center"].get_query = function() { return { filters:{ - "company": frm.doc.company + "company": frm.doc.company, + "is_group": 0 } } }; diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py index 4e5e9037b3a..1be05a346d0 100644 --- a/erpnext/config/hr.py +++ b/erpnext/config/hr.py @@ -170,6 +170,10 @@ def get_data(): "type": "doctype", "name": "Payroll Period", }, + { + "type": "doctype", + "name": "Income Tax Slab", + }, { "type": "doctype", "name": "Salary Component", @@ -209,6 +213,10 @@ def get_data(): "name": "Employee Tax Exemption Proof Submission", "dependencies": ["Employee"] }, + { + "type": "doctype", + "name": "Employee Other Income", + }, { "type": "doctype", "name": "Employee Benefit Application", diff --git a/erpnext/hr/doctype/employee_other_income/__init__.py b/erpnext/hr/doctype/employee_other_income/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.js b/erpnext/hr/doctype/employee_other_income/employee_other_income.js new file mode 100644 index 00000000000..c1a74e863ba --- /dev/null +++ b/erpnext/hr/doctype/employee_other_income/employee_other_income.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Other Income', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.json b/erpnext/hr/doctype/employee_other_income/employee_other_income.json new file mode 100644 index 00000000000..2dd6c10988d --- /dev/null +++ b/erpnext/hr/doctype/employee_other_income/employee_other_income.json @@ -0,0 +1,138 @@ +{ + "actions": [], + "autoname": "HR-INCOME-.######", + "creation": "2020-03-18 15:04:40.767434", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "payroll_period", + "column_break_3", + "company", + "source", + "amount", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fieldname": "payroll_period", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Payroll Period", + "options": "Payroll Period", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "source", + "fieldtype": "Data", + "label": "Source" + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "Company:company:default_currency", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Other Income", + "print_hide": 1, + "read_only": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-03-19 18:06:45.361830", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Other Income", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.py b/erpnext/hr/doctype/employee_other_income/employee_other_income.py new file mode 100644 index 00000000000..ab63c0de623 --- /dev/null +++ b/erpnext/hr/doctype/employee_other_income/employee_other_income.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class EmployeeOtherIncome(Document): + pass diff --git a/erpnext/hr/doctype/employee_other_income/test_employee_other_income.py b/erpnext/hr/doctype/employee_other_income/test_employee_other_income.py new file mode 100644 index 00000000000..2eeca7a23de --- /dev/null +++ b/erpnext/hr/doctype/employee_other_income/test_employee_other_income.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestEmployeeOtherIncome(unittest.TestCase): + pass diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json index e102ff8d705..18fad85c4b3 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json @@ -1,620 +1,180 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-TAX-DEC-.YYYY.-.#####", - "beta": 0, - "creation": "2018-04-13 16:53:36.175504", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-TAX-DEC-.YYYY.-.#####", + "creation": "2018-04-13 16:53:36.175504", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "column_break_2", + "payroll_period", + "company", + "amended_from", + "section_break_8", + "declarations", + "section_break_10", + "total_declared_amount", + "column_break_12", + "total_exemption_amount" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "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 - }, + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fetch_if_empty": 0, - "fieldname": "employee_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": "Employee 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 - }, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fetch_if_empty": 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": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "payroll_period", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Payroll Period", - "length": 0, - "no_copy": 0, - "options": "Payroll Period", - "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 - }, + "fieldname": "payroll_period", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Payroll Period", + "options": "Payroll Period", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.company", - "fetch_if_empty": 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, - "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 - }, + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Employee Tax Exemption Declaration", - "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", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Tax Exemption Declaration", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "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_8", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "declarations", - "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": "Declarations", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Declaration Category", - "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": "declarations", + "fieldtype": "Table", + "label": "Declarations", + "options": "Employee Tax Exemption Declaration Category" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_10", - "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_10", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "total_declared_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 Declared Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_declared_amount", + "fieldtype": "Currency", + "label": "Total Declared Amount", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_12", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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_12", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "total_exemption_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 Exemption Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "other_incomes_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": "Other Incomes", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "income_from_other_sources", - "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": "Income From Other Sources", - "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": "total_exemption_amount", + "fieldtype": "Currency", + "label": "Total Exemption Amount", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-05-11 16:13:50.472670", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Declaration", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-03-18 14:56:25.625717", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Tax Exemption Declaration", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py index f2bba7afed7..fb71a2877a1 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py @@ -8,31 +8,17 @@ from frappe.model.document import Document from frappe import _ from frappe.utils import flt from frappe.model.mapper import get_mapped_doc -from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, calculate_annual_eligible_hra_exemption - -class DuplicateDeclarationError(frappe.ValidationError): pass +from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \ + calculate_annual_eligible_hra_exemption, validate_duplicate_exemption_for_payroll_period class EmployeeTaxExemptionDeclaration(Document): def validate(self): validate_tax_declaration(self.declarations) - self.validate_duplicate() + validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee) self.set_total_declared_amount() self.set_total_exemption_amount() self.calculate_hra_exemption() - def validate_duplicate(self): - duplicate = frappe.db.get_value("Employee Tax Exemption Declaration", - filters = { - "employee": self.employee, - "payroll_period": self.payroll_period, - "name": ["!=", self.name], - "docstatus": ["!=", 2] - } - ) - if duplicate: - frappe.throw(_("Duplicate Tax Declaration of {0} for period {1}") - .format(self.employee, self.payroll_period), DuplicateDeclarationError) - def set_total_declared_amount(self): self.total_declared_amount = 0.0 for d in self.declarations: diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py index 9c87bbd1f30..9549fd1b757 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe, erpnext import unittest from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration import DuplicateDeclarationError +from erpnext.hr.utils import DuplicateDeclarationError class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): def setUp(self): diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json index 9792bd1db61..e13b1ac8888 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json @@ -1,635 +1,140 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-TAX-PRF-.YYYY.-.#####", - "beta": 0, - "creation": "2018-04-13 17:24:11.456132", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-TAX-PRF-.YYYY.-.#####", + "creation": "2018-04-13 17:24:11.456132", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "column_break_2", + "submission_date", + "payroll_period", + "company", + "section_break_5", + "tax_exemption_proofs", + "section_break_10", + "total_actual_amount", + "column_break_12", + "exemption_amount", + "attachment_section", + "attachments", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "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 - }, + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fetch_if_empty": 0, - "fieldname": "employee_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": "Employee 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 - }, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fetch_if_empty": 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": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fetch_if_empty": 0, - "fieldname": "submission_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": "Submission Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "submission_date", + "fieldtype": "Date", + "label": "Submission Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "payroll_period", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Payroll Period", - "length": 0, - "no_copy": 0, - "options": "Payroll Period", - "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 - }, + "fieldname": "payroll_period", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Payroll Period", + "options": "Payroll Period", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.company", - "fetch_if_empty": 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, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "tax_exemption_proofs", - "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": "Tax Exemption Proofs", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Proof Submission Detail", - "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": "tax_exemption_proofs", + "fieldtype": "Table", + "label": "Tax Exemption Proofs", + "options": "Employee Tax Exemption Proof Submission Detail" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_10", - "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_10", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "total_actual_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 Actual Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_actual_amount", + "fieldtype": "Currency", + "label": "Total Actual Amount", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_12", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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_12", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "exemption_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 Exemption Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "exemption_amount", + "fieldtype": "Currency", + "label": "Total Exemption Amount", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "other_incomes_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": "Other Incomes", - "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": "attachment_section", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "income_from_other_sources", - "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": "Income From Other Sources", - "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": "attachments", + "fieldtype": "Attach", + "label": "Attachments" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "attachment_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "attachments", - "fieldtype": "Attach", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Attachments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Employee Tax Exemption Proof Submission", - "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", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Tax Exemption Proof Submission", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-05-13 12:17:18.045171", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Proof Submission", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-03-18 14:55:51.420016", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Tax Exemption Proof Submission", + "owner": "Administrator", "permissions": [ { "amend": 1, diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py index 97ceb63476b..5bc33a65f2c 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py @@ -7,7 +7,8 @@ import frappe from frappe.model.document import Document from frappe import _ from frappe.utils import flt -from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, calculate_hra_exemption_for_period +from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \ + calculate_hra_exemption_for_period, validate_duplicate_exemption_for_payroll_period class EmployeeTaxExemptionProofSubmission(Document): def validate(self): @@ -15,6 +16,7 @@ class EmployeeTaxExemptionProofSubmission(Document): self.set_total_actual_amount() self.set_total_exemption_amount() self.calculate_hra_exemption() + validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee) def set_total_actual_amount(self): self.total_actual_amount = flt(self.get("house_rent_payment_amount")) @@ -32,4 +34,4 @@ class EmployeeTaxExemptionProofSubmission(Document): self.exemption_amount += hra_exemption["total_eligible_hra_exemption"] self.monthly_hra_exemption = hra_exemption["monthly_exemption"] self.monthly_house_rent = hra_exemption["monthly_house_rent"] - self.total_eligible_hra_exemption = hra_exemption["total_eligible_hra_exemption"] \ No newline at end of file + self.total_eligible_hra_exemption = hra_exemption["total_eligible_hra_exemption"] diff --git a/erpnext/hr/doctype/income_tax_slab/__init__.py b/erpnext/hr/doctype/income_tax_slab/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.js b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.js new file mode 100644 index 00000000000..73a54eb8dd9 --- /dev/null +++ b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.js @@ -0,0 +1,6 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Income Tax Slab', { + +}); diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json new file mode 100644 index 00000000000..f74315f32e9 --- /dev/null +++ b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json @@ -0,0 +1,152 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2020-03-17 16:50:35.564915", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "effective_from", + "company", + "column_break_3", + "allow_tax_exemption", + "standard_tax_exemption_amount", + "disabled", + "amended_from", + "taxable_salary_slabs_section", + "slabs", + "taxes_and_charges_on_income_tax_section", + "other_taxes_and_charges" + ], + "fields": [ + { + "fieldname": "effective_from", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Effective from", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "If enabled, Tax Exemption Declaration will be considered for income tax calculation.", + "fieldname": "allow_tax_exemption", + "fieldtype": "Check", + "label": "Allow Tax Exemption" + }, + { + "fieldname": "taxable_salary_slabs_section", + "fieldtype": "Section Break", + "label": "Taxable Salary Slabs" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Income Tax Slab", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "slabs", + "fieldtype": "Table", + "label": "Taxable Salary Slabs", + "options": "Taxable Salary Slab", + "reqd": 1 + }, + { + "allow_on_submit": 1, + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "depends_on": "allow_tax_exemption", + "fieldname": "standard_tax_exemption_amount", + "fieldtype": "Currency", + "label": "Standard Tax Exemption Amount", + "options": "Company:company:default_currency" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "collapsible": 1, + "collapsible_depends_on": "other_taxes_and_charges", + "fieldname": "taxes_and_charges_on_income_tax_section", + "fieldtype": "Section Break", + "label": "Taxes and Charges on Income Tax" + }, + { + "fieldname": "other_taxes_and_charges", + "fieldtype": "Table", + "label": "Other Taxes and Charges", + "options": "Income Tax Slab Other Charges" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-04-29 15:08:21.436120", + "modified_by": "Administrator", + "module": "HR", + "name": "Income Tax Slab", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.py b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.py new file mode 100644 index 00000000000..253f023f68b --- /dev/null +++ b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class IncomeTaxSlab(Document): + pass diff --git a/erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py b/erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py new file mode 100644 index 00000000000..deaaf650a96 --- /dev/null +++ b/erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestIncomeTaxSlab(unittest.TestCase): + pass diff --git a/erpnext/hr/doctype/income_tax_slab_other_charges/__init__.py b/erpnext/hr/doctype/income_tax_slab_other_charges/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json b/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json new file mode 100644 index 00000000000..b23fb3dc317 --- /dev/null +++ b/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json @@ -0,0 +1,75 @@ +{ + "actions": [], + "creation": "2020-04-24 11:46:59.041180", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "description", + "column_break_2", + "percent", + "conditions_section", + "min_taxable_income", + "column_break_7", + "max_taxable_income" + ], + "fields": [ + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "columns": 2, + "fieldname": "min_taxable_income", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Min Taxable Income", + "options": "Company:company:default_currency" + }, + { + "columns": 4, + "fieldname": "description", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Description", + "reqd": 1 + }, + { + "columns": 2, + "fieldname": "percent", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "Percent", + "reqd": 1 + }, + { + "fieldname": "conditions_section", + "fieldtype": "Section Break", + "label": "Conditions" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "columns": 2, + "fieldname": "max_taxable_income", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Max Taxable Income", + "options": "Company:company:default_currency" + } + ], + "istable": 1, + "links": [], + "modified": "2020-04-24 13:27:43.598967", + "modified_by": "Administrator", + "module": "HR", + "name": "Income Tax Slab Other Charges", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py b/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py new file mode 100644 index 00000000000..b4098ecbf3e --- /dev/null +++ b/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class IncomeTaxSlabOtherCharges(Document): + pass diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.json b/erpnext/hr/doctype/payroll_period/payroll_period.json index c9bac095f9f..c0fa506e7f0 100644 --- a/erpnext/hr/doctype/payroll_period/payroll_period.json +++ b/erpnext/hr/doctype/payroll_period/payroll_period.json @@ -1,401 +1,102 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "Prompt", - "beta": 0, - "creation": "2018-04-13 15:18:53.698553", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "autoname": "Prompt", + "creation": "2018-04-13 15:18:53.698553", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "column_break_2", + "start_date", + "end_date", + "section_break_5", + "periods" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "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 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "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": 0, - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "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": 0, - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "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": "Payroll Periods", - "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_5", + "fieldtype": "Section Break", + "hidden": 1, + "label": "Payroll Periods" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "periods", - "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": "Payroll Periods", - "length": 0, - "no_copy": 0, - "options": "Payroll Period Date", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_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, - "label": "Taxable Salary Slabs", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "taxable_salary_slabs", - "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": "Taxable Salary Slabs", - "length": 0, - "no_copy": 0, - "options": "Taxable Salary Slab", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "standard_tax_exemption_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": "Standard Tax Exemption Amount", - "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": "periods", + "fieldtype": "Table", + "label": "Payroll Periods", + "options": "Payroll Period Date" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-26 01:45:03.160929", - "modified_by": "Administrator", - "module": "HR", - "name": "Payroll Period", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-03-18 18:13:23.859980", + "modified_by": "Administrator", + "module": "HR", + "name": "Payroll Period", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "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": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, "write": 1 - }, + }, { - "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": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.py b/erpnext/hr/doctype/payroll_period/payroll_period.py index c1769591eaf..6956c382854 100644 --- a/erpnext/hr/doctype/payroll_period/payroll_period.py +++ b/erpnext/hr/doctype/payroll_period/payroll_period.py @@ -45,8 +45,9 @@ class PayrollPeriod(Document): + _(") for {0}").format(self.company) frappe.throw(msg) -def get_payroll_period_days(start_date, end_date, employee): - company = frappe.db.get_value("Employee", employee, "company") +def get_payroll_period_days(start_date, end_date, employee, company=None): + if not company: + company = frappe.db.get_value("Employee", employee, "company") payroll_period = frappe.db.sql(""" select name, start_date, end_date from `tabPayroll Period` diff --git a/erpnext/hr/doctype/salary_component/salary_component.json b/erpnext/hr/doctype/salary_component/salary_component.json index 986030d8c58..97c46c829e7 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.json +++ b/erpnext/hr/doctype/salary_component/salary_component.json @@ -1,264 +1,263 @@ { - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:salary_component", - "creation": "2016-06-30 15:42:43.631931", - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "salary_component", - "salary_component_abbr", - "type", - "description", - "column_break_4", - "is_payable", - "depends_on_payment_days", - "is_tax_applicable", - "deduct_full_tax_on_selected_payroll_date", - "round_to_the_nearest_integer", - "statistical_component", - "do_not_include_in_total", - "disabled", - "flexible_benefits", - "is_flexible_benefit", - "max_benefit_amount", - "column_break_9", - "pay_against_benefit_claim", - "only_tax_impact", - "create_separate_payment_entry_against_benefit_claim", - "section_break_11", - "variable_based_on_taxable_salary", - "section_break_5", - "accounts", - "condition_and_formula", - "condition", - "amount", - "amount_based_on_formula", - "formula", - "column_break_28", - "help" - ], - "fields": [ - { - "fieldname": "salary_component", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Name", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "salary_component_abbr", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Abbr", - "print_width": "120px", - "reqd": 1, - "width": "120px" - }, - { - "fieldname": "type", - "fieldtype": "Select", - "in_standard_filter": 1, - "label": "Type", - "options": "Earning\nDeduction", - "reqd": 1 - }, - { - "default": "1", - "depends_on": "eval:doc.type == \"Earning\"", - "fieldname": "is_tax_applicable", - "fieldtype": "Check", - "label": "Is Tax Applicable" - }, - { - "default": "1", - "fieldname": "is_payable", - "fieldtype": "Check", - "label": "Is Payable" - }, - { - "default": "1", - "fieldname": "depends_on_payment_days", - "fieldtype": "Check", - "label": "Depends on Payment Days", - "print_hide": 1 - }, - { - "default": "0", - "fieldname": "do_not_include_in_total", - "fieldtype": "Check", - "label": "Do Not Include in Total" - }, - { - "default": "0", - "depends_on": "is_tax_applicable", - "fieldname": "deduct_full_tax_on_selected_payroll_date", - "fieldtype": "Check", - "label": "Deduct Full Tax on Selected Payroll Date" - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "disabled", - "fieldtype": "Check", - "label": "Disabled" - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "in_list_view": 1, - "label": "Description" - }, - { - "default": "0", - "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", - "fieldname": "statistical_component", - "fieldtype": "Check", - "label": "Statistical Component" - }, - { - "depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1", - "fieldname": "flexible_benefits", - "fieldtype": "Section Break", - "label": "Flexible Benefits" - }, - { - "default": "0", - "fieldname": "is_flexible_benefit", - "fieldtype": "Check", - "label": "Is Flexible Benefit" - }, - { - "depends_on": "is_flexible_benefit", - "fieldname": "max_benefit_amount", - "fieldtype": "Currency", - "label": "Max Benefit Amount (Yearly)" - }, - { - "fieldname": "column_break_9", - "fieldtype": "Column Break" - }, - { - "default": "0", - "depends_on": "is_flexible_benefit", - "fieldname": "pay_against_benefit_claim", - "fieldtype": "Check", - "label": "Pay Against Benefit Claim" - }, - { - "default": "0", - "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.create_separate_payment_entry_against_benefit_claim !=1", - "fieldname": "only_tax_impact", - "fieldtype": "Check", - "label": "Only Tax Impact (Cannot Claim But Part of Taxable Income)" - }, - { - "default": "0", - "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.only_tax_impact !=1", - "fieldname": "create_separate_payment_entry_against_benefit_claim", - "fieldtype": "Check", - "label": "Create Separate Payment Entry Against Benefit Claim" - }, - { - "depends_on": "eval:doc.type=='Deduction'", - "fieldname": "section_break_11", - "fieldtype": "Section Break" - }, - { - "default": "0", - "fieldname": "variable_based_on_taxable_salary", - "fieldtype": "Check", - "label": "Variable Based On Taxable Salary" - }, - { - "depends_on": "eval:doc.statistical_component != 1", - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "label": "Accounts" - }, - { - "fieldname": "accounts", - "fieldtype": "Table", - "label": "Accounts", - "options": "Salary Component Account" - }, - { - "collapsible": 1, - "depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1", - "fieldname": "condition_and_formula", - "fieldtype": "Section Break", - "label": "Condition and Formula" - }, - { - "fieldname": "condition", - "fieldtype": "Code", - "label": "Condition" - }, - { - "default": "0", - "fieldname": "amount_based_on_formula", - "fieldtype": "Check", - "label": "Amount based on formula" - }, - { - "depends_on": "amount_based_on_formula", - "fieldname": "formula", - "fieldtype": "Code", - "label": "Formula" - }, - { - "depends_on": "eval:doc.amount_based_on_formula!==1", - "fieldname": "amount", - "fieldtype": "Currency", - "label": "Amount" - }, - { - "fieldname": "column_break_28", - "fieldtype": "Column Break" - }, - { - "fieldname": "help", - "fieldtype": "HTML", - "label": "Help", - "options": "

Help

\n\n

Notes:

\n\n
    \n
  1. Use field base for using base salary of the Employee
  2. \n
  3. Use Salary Component abbreviations in conditions and formulas. BS = Basic Salary
  4. \n
  5. Use field name for employee details in conditions and formulas. Employment Type = employment_typeBranch = branch
  6. \n
  7. Use field name from Salary Slip in conditions and formulas. Payment Days = payment_daysLeave without pay = leave_without_pay
  8. \n
  9. Direct Amount can also be entered based on Condtion. See example 3
\n\n

Examples

\n
    \n
  1. Calculating Basic Salary based on base\n
    Condition: base < 10000
    \n
    Formula: base * .2
  2. \n
  3. Calculating HRA based on Basic SalaryBS \n
    Condition: BS > 2000
    \n
    Formula: BS * .1
  4. \n
  5. Calculating TDS based on Employment Typeemployment_type \n
    Condition: employment_type==\"Intern\"
    \n
    Amount: 1000
  6. \n
" - }, - { - "default": "0", - "fieldname": "round_to_the_nearest_integer", - "fieldtype": "Check", - "label": "Round to the Nearest Integer" - } - ], - "icon": "fa fa-flag", - "modified": "2019-06-05 11:34:14.231228", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Component", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - }, - { - "read": 1, - "role": "Employee" - } - ], - "sort_field": "modified", - "sort_order": "DESC" - } \ No newline at end of file + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:salary_component", + "creation": "2016-06-30 15:42:43.631931", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "salary_component", + "salary_component_abbr", + "type", + "description", + "column_break_4", + "depends_on_payment_days", + "is_tax_applicable", + "deduct_full_tax_on_selected_payroll_date", + "variable_based_on_taxable_salary", + "exempted_from_income_tax", + "round_to_the_nearest_integer", + "statistical_component", + "do_not_include_in_total", + "disabled", + "flexible_benefits", + "is_flexible_benefit", + "max_benefit_amount", + "column_break_9", + "pay_against_benefit_claim", + "only_tax_impact", + "create_separate_payment_entry_against_benefit_claim", + "section_break_5", + "accounts", + "condition_and_formula", + "condition", + "amount", + "amount_based_on_formula", + "formula", + "column_break_28", + "help" + ], + "fields": [ + { + "fieldname": "salary_component", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "salary_component_abbr", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Abbr", + "print_width": "120px", + "reqd": 1, + "width": "120px" + }, + { + "fieldname": "type", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Type", + "options": "Earning\nDeduction", + "reqd": 1 + }, + { + "default": "1", + "depends_on": "eval:doc.type == \"Earning\"", + "fieldname": "is_tax_applicable", + "fieldtype": "Check", + "label": "Is Tax Applicable" + }, + { + "default": "1", + "fieldname": "depends_on_payment_days", + "fieldtype": "Check", + "label": "Depends on Payment Days", + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "do_not_include_in_total", + "fieldtype": "Check", + "label": "Do Not Include in Total" + }, + { + "default": "0", + "depends_on": "eval:doc.is_tax_applicable && doc.type=='Earning'", + "fieldname": "deduct_full_tax_on_selected_payroll_date", + "fieldtype": "Check", + "label": "Deduct Full Tax on Selected Payroll Date" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Description" + }, + { + "default": "0", + "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", + "fieldname": "statistical_component", + "fieldtype": "Check", + "label": "Statistical Component" + }, + { + "depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1", + "fieldname": "flexible_benefits", + "fieldtype": "Section Break", + "label": "Flexible Benefits" + }, + { + "default": "0", + "fieldname": "is_flexible_benefit", + "fieldtype": "Check", + "label": "Is Flexible Benefit" + }, + { + "depends_on": "is_flexible_benefit", + "fieldname": "max_benefit_amount", + "fieldtype": "Currency", + "label": "Max Benefit Amount (Yearly)" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "is_flexible_benefit", + "fieldname": "pay_against_benefit_claim", + "fieldtype": "Check", + "label": "Pay Against Benefit Claim" + }, + { + "default": "0", + "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.create_separate_payment_entry_against_benefit_claim !=1", + "fieldname": "only_tax_impact", + "fieldtype": "Check", + "label": "Only Tax Impact (Cannot Claim But Part of Taxable Income)" + }, + { + "default": "0", + "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.only_tax_impact !=1", + "fieldname": "create_separate_payment_entry_against_benefit_claim", + "fieldtype": "Check", + "label": "Create Separate Payment Entry Against Benefit Claim" + }, + { + "default": "0", + "depends_on": "eval:doc.type == \"Deduction\"", + "fieldname": "variable_based_on_taxable_salary", + "fieldtype": "Check", + "label": "Variable Based On Taxable Salary" + }, + { + "depends_on": "eval:doc.statistical_component != 1", + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "label": "Accounts" + }, + { + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounts", + "options": "Salary Component Account" + }, + { + "collapsible": 1, + "depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1", + "fieldname": "condition_and_formula", + "fieldtype": "Section Break", + "label": "Condition and Formula" + }, + { + "fieldname": "condition", + "fieldtype": "Code", + "label": "Condition" + }, + { + "default": "0", + "fieldname": "amount_based_on_formula", + "fieldtype": "Check", + "label": "Amount based on formula" + }, + { + "depends_on": "amount_based_on_formula", + "fieldname": "formula", + "fieldtype": "Code", + "label": "Formula" + }, + { + "depends_on": "eval:doc.amount_based_on_formula!==1", + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "fieldname": "help", + "fieldtype": "HTML", + "label": "Help", + "options": "

Help

\n\n

Notes:

\n\n
    \n
  1. Use field base for using base salary of the Employee
  2. \n
  3. Use Salary Component abbreviations in conditions and formulas. BS = Basic Salary
  4. \n
  5. Use field name for employee details in conditions and formulas. Employment Type = employment_typeBranch = branch
  6. \n
  7. Use field name from Salary Slip in conditions and formulas. Payment Days = payment_daysLeave without pay = leave_without_pay
  8. \n
  9. Direct Amount can also be entered based on Condtion. See example 3
\n\n

Examples

\n
    \n
  1. Calculating Basic Salary based on base\n
    Condition: base < 10000
    \n
    Formula: base * .2
  2. \n
  3. Calculating HRA based on Basic SalaryBS \n
    Condition: BS > 2000
    \n
    Formula: BS * .1
  4. \n
  5. Calculating TDS based on Employment Typeemployment_type \n
    Condition: employment_type==\"Intern\"
    \n
    Amount: 1000
  6. \n
" + }, + { + "default": "0", + "fieldname": "round_to_the_nearest_integer", + "fieldtype": "Check", + "label": "Round to the Nearest Integer" + }, + { + "default": "0", + "depends_on": "eval:doc.type == \"Deduction\" && !doc.variable_based_on_taxable_salary", + "description": "If checked, the full amount will be deducted from taxable income before calculating income tax without any declaration or proof submission.", + "fieldname": "exempted_from_income_tax", + "fieldtype": "Check", + "label": "Exempted from Income Tax" + } + ], + "icon": "fa fa-flag", + "links": [], + "modified": "2020-04-28 15:46:45.252945", + "modified_by": "Administrator", + "module": "HR", + "name": "Salary Component", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Employee" + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_component/test_records.json b/erpnext/hr/doctype/salary_component/test_records.json index 7b22b481f36..104b44ffa1b 100644 --- a/erpnext/hr/doctype/salary_component/test_records.json +++ b/erpnext/hr/doctype/salary_component/test_records.json @@ -3,14 +3,12 @@ "doctype": "Salary Component", "salary_component": "_Test Basic Salary", "type": "Earning", - "is_payable": 1, "is_tax_applicable": 1 }, { "doctype": "Salary Component", "salary_component": "_Test Allowance", "type": "Earning", - "is_payable": 1, "is_tax_applicable": 1 }, { @@ -27,14 +25,12 @@ "doctype": "Salary Component", "salary_component": "Basic", "type": "Earning", - "is_payable": 1, "is_tax_applicable": 1 }, { "doctype": "Salary Component", "salary_component": "Leave Encashment", "type": "Earning", - "is_payable": 1, "is_tax_applicable": 1 } ] \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_component/test_salary_component.py b/erpnext/hr/doctype/salary_component/test_salary_component.py index 965cc9e9ffd..4f7db0c71ca 100644 --- a/erpnext/hr/doctype/salary_component/test_salary_component.py +++ b/erpnext/hr/doctype/salary_component/test_salary_component.py @@ -18,6 +18,5 @@ def create_salary_component(component_name, **args): "doctype": "Salary Component", "salary_component": component_name, "type": args.get("type") or "Earning", - "is_payable": args.get("is_payable") or 1, "is_tax_applicable": args.get("is_tax_applicable") or 1 }).insert() diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.json b/erpnext/hr/doctype/salary_detail/salary_detail.json index edf2786821e..545f56a0b60 100644 --- a/erpnext/hr/doctype/salary_detail/salary_detail.json +++ b/erpnext/hr/doctype/salary_detail/salary_detail.json @@ -1,765 +1,216 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-06-30 15:32:36.385111", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "creation": "2016-06-30 15:32:36.385111", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "salary_component", + "abbr", + "statistical_component", + "column_break_3", + "deduct_full_tax_on_selected_payroll_date", + "depends_on_payment_days", + "is_tax_applicable", + "exempted_from_income_tax", + "is_flexible_benefit", + "variable_based_on_taxable_salary", + "section_break_2", + "condition", + "amount_based_on_formula", + "formula", + "amount", + "do_not_include_in_total", + "default_amount", + "additional_amount", + "tax_on_flexible_benefit", + "tax_on_additional_salary", + "section_break_11", + "condition_and_formula_help" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "salary_component", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Component", - "length": 0, - "no_copy": 0, - "options": "Salary Component", - "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 - }, + "fieldname": "salary_component", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Component", + "options": "Salary Component", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "default": "", - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_from": "salary_component.salary_component_abbr", - "fetch_if_empty": 0, - "fieldname": "abbr", - "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": "Abbr", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 1, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fetch_from": "salary_component.salary_component_abbr", + "fieldname": "abbr", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Abbr", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", - "fetch_from": "salary_component.statistical_component", - "fetch_if_empty": 0, - "fieldname": "statistical_component", - "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": "Statistical Component", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", + "fetch_from": "salary_component.statistical_component", + "fieldname": "statistical_component", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Statistical Component" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "salary_component.is_tax_applicable", - "fetch_if_empty": 0, - "fieldname": "is_tax_applicable", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Tax Applicable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:doc.parentfield=='earnings'", + "fetch_from": "salary_component.is_tax_applicable", + "fieldname": "is_tax_applicable", + "fieldtype": "Check", + "label": "Is Tax Applicable", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "salary_component.is_flexible_benefit", - "fetch_if_empty": 0, - "fieldname": "is_flexible_benefit", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Flexible Benefit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:doc.parentfield=='earnings'", + "fetch_from": "salary_component.is_flexible_benefit", + "fieldname": "is_flexible_benefit", + "fieldtype": "Check", + "label": "Is Flexible Benefit", + "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, - "default": "", - "fetch_from": "salary_component.variable_based_on_taxable_salary", - "fetch_if_empty": 0, - "fieldname": "variable_based_on_taxable_salary", - "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": "Variable Based On Taxable Salary", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:doc.parentfield=='deductions'", + "fetch_from": "salary_component.variable_based_on_taxable_salary", + "fieldname": "variable_based_on_taxable_salary", + "fieldtype": "Check", + "label": "Variable Based On Taxable Salary", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_from": "salary_component.depends_on_payment_days", - "fetch_if_empty": 0, - "fieldname": "depends_on_payment_days", - "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": "Depends on Payment Days", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fetch_from": "salary_component.depends_on_payment_days", + "fieldname": "depends_on_payment_days", + "fieldtype": "Check", + "label": "Depends on Payment Days", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "deduct_full_tax_on_selected_payroll_date", - "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": "Deduct Full Tax on Selected Payroll Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "deduct_full_tax_on_selected_payroll_date", + "fieldtype": "Check", + "label": "Deduct Full Tax on Selected Payroll Date", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_flexible_benefit != 1", - "fetch_if_empty": 0, - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "depends_on": "eval:doc.is_flexible_benefit != 1", + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_if_empty": 0, - "fieldname": "condition", - "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": "Condition", - "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_on_submit": 1, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "condition", + "fieldtype": "Code", + "label": "Condition" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_from": "", - "fetch_if_empty": 0, - "fieldname": "amount_based_on_formula", - "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": "Amount based on formula", - "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 - }, + "default": "0", + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "amount_based_on_formula", + "fieldtype": "Check", + "label": "Amount based on formula" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "depends_on": "eval:doc.amount_based_on_formula!==0 && doc.parenttype==='Salary Structure'", - "description": "", - "fetch_if_empty": 0, - "fieldname": "formula", - "fieldtype": "Code", - "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": "Formula", - "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_on_submit": 1, + "depends_on": "eval:doc.amount_based_on_formula!==0 && doc.parenttype==='Salary Structure'", + "fieldname": "formula", + "fieldtype": "Code", + "in_list_view": 1, + "label": "Formula" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.amount_based_on_formula!==1 || doc.parenttype==='Salary Slip'", - "fetch_if_empty": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.amount_based_on_formula!==1 || doc.parenttype==='Salary Slip'", + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "Company:company:default_currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "do_not_include_in_total", - "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": "Do not include in total", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "do_not_include_in_total", + "fieldtype": "Check", + "label": "Do not include in total" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_if_empty": 0, - "fieldname": "default_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": "Default Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "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 - }, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "default_amount", + "fieldtype": "Currency", + "label": "Default Amount", + "options": "Company:company:default_currency", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_from": "", - "fetch_if_empty": 0, - "fieldname": "additional_amount", - "fieldtype": "Currency", - "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": "Additional Amount", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "additional_amount", + "fieldtype": "Currency", + "hidden": 1, + "label": "Additional Amount", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", - "fetch_if_empty": 0, - "fieldname": "tax_on_flexible_benefit", - "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": "Tax on flexible benefit", - "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 - }, + "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", + "fieldname": "tax_on_flexible_benefit", + "fieldtype": "Currency", + "label": "Tax on flexible benefit", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", - "fetch_if_empty": 0, - "fieldname": "tax_on_additional_salary", - "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": "Tax on additional salary", - "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 - }, + "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", + "fieldname": "tax_on_additional_salary", + "fieldtype": "Currency", + "label": "Tax on additional salary", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_if_empty": 0, - "fieldname": "section_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "section_break_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_if_empty": 0, - "fieldname": "condition_and_formula_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": "Condition and Formula Help", - "length": 0, - "no_copy": 0, - "options": "

Condition and Formula Help

\n\n

Notes:

\n\n
    \n
  1. Use field base for using base salary of the Employee
  2. \n
  3. Use Salary Component abbreviations in conditions and formulas. BS = Basic Salary
  4. \n
  5. Use field name for employee details in conditions and formulas. Employment Type = employment_typeBranch = branch
  6. \n
  7. Use field name from Salary Slip in conditions and formulas. Payment Days = payment_daysLeave without pay = leave_without_pay
  8. \n
  9. Direct Amount can also be entered based on Condtion. See example 3
\n\n

Examples

\n
    \n
  1. Calculating Basic Salary based on base\n
    Condition: base < 10000
    \n
    Formula: base * .2
  2. \n
  3. Calculating HRA based on Basic SalaryBS \n
    Condition: BS > 2000
    \n
    Formula: BS * .1
  4. \n
  5. Calculating TDS based on Employment Typeemployment_type \n
    Condition: employment_type==\"Intern\"
    \n
    Amount: 1000
  6. \n
", - "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 + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "condition_and_formula_help", + "fieldtype": "HTML", + "label": "Condition and Formula Help", + "options": "

Condition and Formula Help

\n\n

Notes:

\n\n
    \n
  1. Use field base for using base salary of the Employee
  2. \n
  3. Use Salary Component abbreviations in conditions and formulas. BS = Basic Salary
  4. \n
  5. Use field name for employee details in conditions and formulas. Employment Type = employment_typeBranch = branch
  6. \n
  7. Use field name from Salary Slip in conditions and formulas. Payment Days = payment_daysLeave without pay = leave_without_pay
  8. \n
  9. Direct Amount can also be entered based on Condtion. See example 3
\n\n

Examples

\n
    \n
  1. Calculating Basic Salary based on base\n
    Condition: base < 10000
    \n
    Formula: base * .2
  2. \n
  3. Calculating HRA based on Basic SalaryBS \n
    Condition: BS > 2000
    \n
    Formula: BS * .1
  4. \n
  5. Calculating TDS based on Employment Typeemployment_type \n
    Condition: employment_type==\"Intern\"
    \n
    Amount: 1000
  6. \n
" + }, + { + "default": "0", + "depends_on": "eval:doc.parentfield=='deductions'", + "fetch_from": "salary_component.exempted_from_income_tax", + "fieldname": "exempted_from_income_tax", + "fieldtype": "Check", + "label": "Exempted from Income Tax", + "read_only": 1 } - ], - "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-05-11 17:33:08.508653", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Detail", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-04-24 20:00:16.475295", + "modified_by": "Administrator", + "module": "HR", + "name": "Salary Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 6157daff1e0..452aa74281e 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -450,7 +450,8 @@ class SalarySlip(TransactionBase): 'is_flexible_benefit': struct_row.is_flexible_benefit, 'variable_based_on_taxable_salary': struct_row.variable_based_on_taxable_salary, 'deduct_full_tax_on_selected_payroll_date': struct_row.deduct_full_tax_on_selected_payroll_date, - 'additional_amount': amount if struct_row.get("is_additional_component") else 0 + 'additional_amount': amount if struct_row.get("is_additional_component") else 0, + 'exempted_from_income_tax': struct_row.exempted_from_income_tax }) else: if struct_row.get("is_additional_component"): @@ -481,20 +482,23 @@ class SalarySlip(TransactionBase): return self.calculate_variable_tax(payroll_period, tax_component) def calculate_variable_tax(self, payroll_period, tax_component): + # get Tax slab from salary structure assignment for the employee and payroll period + tax_slab = self.get_income_tax_slabs(payroll_period) + # get remaining numbers of sub-period (period for which one salary is processed) remaining_sub_periods = get_period_factor(self.employee, self.start_date, self.end_date, self.payroll_frequency, payroll_period)[1] - # get taxable_earnings, paid_taxes for previous period - previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date, self.start_date) + previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date, + self.start_date, tax_slab.allow_tax_exemption) previous_total_paid_taxes = self.get_tax_paid_in_period(payroll_period.start_date, self.start_date, tax_component) # get taxable_earnings for current period (all days) - current_taxable_earnings = self.get_taxable_earnings() + current_taxable_earnings = self.get_taxable_earnings(tax_slab.allow_tax_exemption) future_structured_taxable_earnings = current_taxable_earnings.taxable_earnings * (math.ceil(remaining_sub_periods) - 1) # get taxable_earnings, addition_earnings for current actual payment days - current_taxable_earnings_for_payment_days = self.get_taxable_earnings(based_on_payment_days=1) + current_taxable_earnings_for_payment_days = self.get_taxable_earnings(tax_slab.allow_tax_exemption, based_on_payment_days=1) current_structured_taxable_earnings = current_taxable_earnings_for_payment_days.taxable_earnings current_additional_earnings = current_taxable_earnings_for_payment_days.additional_income current_additional_earnings_with_full_tax = current_taxable_earnings_for_payment_days.additional_income_with_full_tax @@ -506,23 +510,27 @@ class SalarySlip(TransactionBase): unclaimed_taxable_benefits += current_taxable_earnings_for_payment_days.flexi_benefits # Total exemption amount based on tax exemption declaration - total_exemption_amount, other_incomes = self.get_total_exemption_amount_and_other_incomes(payroll_period) + total_exemption_amount = self.get_total_exemption_amount(payroll_period, tax_slab) + + #Employee Other Incomes + other_incomes = self.get_income_form_other_sources(payroll_period) or 0.0 # Total taxable earnings including additional and other incomes total_taxable_earnings = previous_taxable_earnings + current_structured_taxable_earnings + future_structured_taxable_earnings \ + current_additional_earnings + other_incomes + unclaimed_taxable_benefits - total_exemption_amount - + # Total taxable earnings without additional earnings with full tax total_taxable_earnings_without_full_tax_addl_components = total_taxable_earnings - current_additional_earnings_with_full_tax # Structured tax amount - total_structured_tax_amount = self.calculate_tax_by_tax_slab(payroll_period, total_taxable_earnings_without_full_tax_addl_components) + total_structured_tax_amount = self.calculate_tax_by_tax_slab( + total_taxable_earnings_without_full_tax_addl_components, tax_slab) current_structured_tax_amount = (total_structured_tax_amount - previous_total_paid_taxes) / remaining_sub_periods - + # Total taxable earnings with additional earnings with full tax full_tax_on_additional_earnings = 0.0 if current_additional_earnings_with_full_tax: - total_tax_amount = self.calculate_tax_by_tax_slab(payroll_period, total_taxable_earnings) + total_tax_amount = self.calculate_tax_by_tax_slab(total_taxable_earnings, tax_slab) full_tax_on_additional_earnings = total_tax_amount - total_structured_tax_amount current_tax_amount = current_structured_tax_amount + full_tax_on_additional_earnings @@ -531,12 +539,30 @@ class SalarySlip(TransactionBase): return current_tax_amount - def get_taxable_earnings_for_prev_period(self, start_date, end_date): + def get_income_tax_slabs(self, payroll_period): + income_tax_slab, ss_assignment_name = frappe.db.get_value("Salary Structure Assignment", + {"employee": self.employee, "salary_structure": self.salary_structure, "docstatus": 1}, ["income_tax_slab", 'name']) + + if not income_tax_slab: + frappe.throw(_("Income Tax Slab not set in Salary Structure Assignment: {0}").format(ss_assignment_name)) + + income_tax_slab_doc = frappe.get_doc("Income Tax Slab", income_tax_slab) + if income_tax_slab_doc.disabled: + frappe.throw(_("Income Tax Slab: {0} is disabled").format(income_tax_slab)) + + if getdate(income_tax_slab_doc.effective_from) > getdate(payroll_period.start_date): + frappe.throw(_("Income Tax Slab must be effective on or before Payroll Period Start Date: {0}") + .format(payroll_period.start_date)) + + return income_tax_slab_doc + + + def get_taxable_earnings_for_prev_period(self, start_date, end_date, allow_tax_exemption=False): taxable_earnings = frappe.db.sql(""" select sum(sd.amount) from `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name - where + where sd.parentfield='earnings' and sd.is_tax_applicable=1 and is_flexible_benefit=0 @@ -549,7 +575,30 @@ class SalarySlip(TransactionBase): "from_date": start_date, "to_date": end_date }) - return flt(taxable_earnings[0][0]) if taxable_earnings else 0 + taxable_earnings = flt(taxable_earnings[0][0]) if taxable_earnings else 0 + + exempted_amount = 0 + if allow_tax_exemption: + exempted_amount = frappe.db.sql(""" + select sum(sd.amount) + from + `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name + where + sd.parentfield='deductions' + and sd.exempted_from_income_tax=1 + and is_flexible_benefit=0 + and ss.docstatus=1 + and ss.employee=%(employee)s + and ss.start_date between %(from_date)s and %(to_date)s + and ss.end_date between %(from_date)s and %(to_date)s + """, { + "employee": self.employee, + "from_date": start_date, + "to_date": end_date + }) + exempted_amount = flt(exempted_amount[0][0]) if exempted_amount else 0 + + return taxable_earnings - exempted_amount def get_tax_paid_in_period(self, start_date, end_date, tax_component): # find total_tax_paid, tax paid for benefit, additional_salary @@ -575,7 +624,7 @@ class SalarySlip(TransactionBase): return total_tax_paid - def get_taxable_earnings(self, based_on_payment_days=0): + def get_taxable_earnings(self, allow_tax_exemption=False, based_on_payment_days=0): joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee, ["date_of_joining", "relieving_date"]) @@ -609,6 +658,14 @@ class SalarySlip(TransactionBase): else: taxable_earnings += amount + if allow_tax_exemption: + for ded in self.deductions: + if ded.exempted_from_income_tax: + amount = ded.amount + if based_on_payment_days: + amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)[0] + taxable_earnings -= flt(amount) + return frappe._dict({ "taxable_earnings": taxable_earnings, "additional_income": additional_income, @@ -671,40 +728,63 @@ class SalarySlip(TransactionBase): return total_benefits_paid - total_benefits_claimed - def get_total_exemption_amount_and_other_incomes(self, payroll_period): - total_exemption_amount, other_incomes = 0, 0 - if self.deduct_tax_for_unsubmitted_tax_exemption_proof: - exemption_proof = frappe.db.get_value("Employee Tax Exemption Proof Submission", - {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, - ["exemption_amount", "income_from_other_sources"]) - if exemption_proof: - total_exemption_amount, other_incomes = exemption_proof - else: - declaration = frappe.db.get_value("Employee Tax Exemption Declaration", - {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, - ["total_exemption_amount", "income_from_other_sources"]) - if declaration: - total_exemption_amount, other_incomes = declaration + def get_total_exemption_amount(self, payroll_period, tax_slab): + total_exemption_amount = 0 + if tax_slab.allow_tax_exemption: + if self.deduct_tax_for_unsubmitted_tax_exemption_proof: + exemption_proof = frappe.db.get_value("Employee Tax Exemption Proof Submission", + {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, + ["exemption_amount"]) + if exemption_proof: + total_exemption_amount = exemption_proof + else: + declaration = frappe.db.get_value("Employee Tax Exemption Declaration", + {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, + ["total_exemption_amount"]) + if declaration: + total_exemption_amount = declaration - return total_exemption_amount, other_incomes + total_exemption_amount += flt(tax_slab.standard_tax_exemption_amount) - def calculate_tax_by_tax_slab(self, payroll_period, annual_taxable_earning): - payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period) - annual_taxable_earning -= flt(payroll_period_obj.standard_tax_exemption_amount) + return total_exemption_amount + + def get_income_form_other_sources(self, payroll_period): + return frappe.get_all("Employee Other Income", + filters={ + "employee": self.employee, + "payroll_period": payroll_period.name, + "company": self.company, + "docstatus": 1 + }, + fields="SUM(amount) as total_amount" + )[0].total_amount + + def calculate_tax_by_tax_slab(self, annual_taxable_earning, tax_slab): data = self.get_data_for_eval() data.update({"annual_taxable_earning": annual_taxable_earning}) - taxable_amount = 0 - for slab in payroll_period_obj.taxable_salary_slabs: + tax_amount = 0 + for slab in tax_slab.slabs: if slab.condition and not self.eval_tax_slab_condition(slab.condition, data): continue - if not slab.to_amount and annual_taxable_earning > slab.from_amount: - taxable_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01 + if not slab.to_amount and annual_taxable_earning >= slab.from_amount: + tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction *.01 continue - if annual_taxable_earning > slab.from_amount and annual_taxable_earning < slab.to_amount: - taxable_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01 - elif annual_taxable_earning > slab.from_amount and annual_taxable_earning > slab.to_amount: - taxable_amount += (slab.to_amount - slab.from_amount) * slab.percent_deduction * .01 - return taxable_amount + if annual_taxable_earning >= slab.from_amount and annual_taxable_earning < slab.to_amount: + tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction *.01 + elif annual_taxable_earning >= slab.from_amount and annual_taxable_earning >= slab.to_amount: + tax_amount += (slab.to_amount - slab.from_amount + 1) * slab.percent_deduction * .01 + + # other taxes and charges on income tax + for d in tax_slab.other_taxes_and_charges: + if flt(d.min_taxable_income) and flt(d.min_taxable_income) > tax_amount: + continue + + if flt(d.max_taxable_income) and flt(d.max_taxable_income) < tax_amount: + continue + + tax_amount += tax_amount * flt(d.percent) / 100 + + return tax_amount def eval_tax_slab_condition(self, condition, data): try: diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index 16a75f473f4..7572e00359a 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -47,10 +47,7 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.payment_days, no_of_days[0]) self.assertEqual(ss.earnings[0].amount, 50000) self.assertEqual(ss.earnings[1].amount, 3000) - self.assertEqual(ss.deductions[0].amount, 5000) - self.assertEqual(ss.deductions[1].amount, 5000) self.assertEqual(ss.gross_pay, 78000) - self.assertEqual(ss.net_pay, 67418.0) def test_salary_slip_with_holidays_excluded(self): no_of_days = self.get_no_of_days() @@ -67,10 +64,7 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.earnings[0].amount, 50000) self.assertEqual(ss.earnings[0].default_amount, 50000) self.assertEqual(ss.earnings[1].amount, 3000) - self.assertEqual(ss.deductions[0].amount, 5000) - self.assertEqual(ss.deductions[1].amount, 5000) self.assertEqual(ss.gross_pay, 78000) - self.assertEqual(ss.net_pay, 67418.0) def test_payment_days(self): no_of_days = self.get_no_of_days() @@ -80,8 +74,8 @@ class TestSalarySlip(unittest.TestCase): # set joinng date in the same month make_employee("test_employee@salary.com") if getdate(nowdate()).day >= 15: - date_of_joining = getdate(add_days(nowdate(),-10)) relieving_date = getdate(add_days(nowdate(),-10)) + date_of_joining = getdate(add_days(nowdate(),-10)) elif getdate(nowdate()).day < 15 and getdate(nowdate()).day >= 5: date_of_joining = getdate(add_days(nowdate(),-3)) relieving_date = getdate(add_days(nowdate(),-3)) @@ -131,9 +125,7 @@ class TestSalarySlip(unittest.TestCase): def test_email_salary_slip(self): frappe.db.sql("delete from `tabEmail Queue`") - hr_settings = frappe.get_doc("HR Settings", "HR Settings") - hr_settings.email_salary_slip_to_employee = 1 - hr_settings.save() + frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 1) make_employee("test_employee@salary.com") ss = make_employee_salary_slip("test_employee@salary.com", "Monthly") @@ -183,8 +175,11 @@ class TestSalarySlip(unittest.TestCase): # as per assigned salary structure 40500 in monthly salary so 236000*5/100/12 frappe.db.sql("""delete from `tabPayroll Period`""") frappe.db.sql("""delete from `tabSalary Component`""") + payroll_period = create_payroll_period() - create_tax_slab(payroll_period) + + create_tax_slab(payroll_period, allow_tax_exemption=True) + employee = make_employee("test_tax@salary.slip") delete_docs = [ "Salary Slip", @@ -210,8 +205,7 @@ class TestSalarySlip(unittest.TestCase): payroll_period, deduct_random=False) tax_paid = get_tax_paid_in_period(employee) - # total taxable income 586000, 250000 @ 5%, 86000 @ 20% ie. 12500 + 17200 - annual_tax = 113568 + annual_tax = 113589.0 try: self.assertEqual(tax_paid, annual_tax) except AssertionError: @@ -235,8 +229,7 @@ class TestSalarySlip(unittest.TestCase): raise # Submit proof for total 120000 - data["proof-1"] = create_proof_submission(employee, payroll_period, 50000) - data["proof-2"] = create_proof_submission(employee, payroll_period, 70000) + data["proof"] = create_proof_submission(employee, payroll_period, 120000) # Submit benefit claim for total 50000 data["benefit-1"] = create_benefit_claim(employee, payroll_period, 15000, "Medical Allowance") @@ -250,7 +243,7 @@ class TestSalarySlip(unittest.TestCase): # total taxable income 416000, 166000 @ 5% ie. 8300 try: - self.assertEqual(tax_paid, 88608) + self.assertEqual(tax_paid, 82389.0) except AssertionError: print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") raise @@ -265,7 +258,7 @@ class TestSalarySlip(unittest.TestCase): # total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200 tax_paid = get_tax_paid_in_period(employee) try: - self.assertEqual(tax_paid, 121211) + self.assertEqual(tax_paid, annual_tax) except AssertionError: print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") raise @@ -307,6 +300,7 @@ def make_employee_salary_slip(user, payroll_frequency, salary_structure=None): from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure if not salary_structure: salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip" + employee = frappe.db.get_value("Employee", {"user_id": user}) salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee) salary_slip = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})}) @@ -431,17 +425,15 @@ def make_deduction_salary_component(setup=False, test_tax=False): { "salary_component": 'Professional Tax', "abbr":'PT', - "condition": 'base > 10000', - "formula": 'base*.1', "type": "Deduction", - "amount_based_on_formula": 1 + "amount": 200, + "exempted_from_income_tax": 1 + }, { "salary_component": 'TDS', "abbr":'T', - "formula": 'base*.1', "type": "Deduction", - "amount_based_on_formula": 1, "depends_on_payment_days": 0, "variable_based_on_taxable_salary": 1, "round_to_the_nearest_integer": 1 @@ -452,9 +444,7 @@ def make_deduction_salary_component(setup=False, test_tax=False): "salary_component": 'TDS', "abbr":'T', "condition": 'employment_type=="Intern"', - "formula": 'base*.1', "type": "Deduction", - "amount_based_on_formula": 1, "round_to_the_nearest_integer": 1 }) if setup or test_tax: @@ -510,29 +500,47 @@ def create_benefit_claim(employee, payroll_period, amount, component): }).submit() return claim_date -def create_tax_slab(payroll_period): - data = [ +def create_tax_slab(payroll_period, effective_date = None, allow_tax_exemption = False, dont_submit = False): + if frappe.db.exists("Income Tax Slab", "Tax Slab: " + payroll_period.name): + return + + slabs = [ { "from_amount": 250000, "to_amount": 500000, - "percent_deduction": 5.2, + "percent_deduction": 5, "condition": "annual_taxable_earning > 500000" }, { "from_amount": 500001, "to_amount": 1000000, - "percent_deduction": 20.8 + "percent_deduction": 20 }, { "from_amount": 1000001, - "percent_deduction": 31.2 + "percent_deduction": 30 } ] - payroll_period.taxable_salary_slabs = [] - for item in data: - payroll_period.append("taxable_salary_slabs", item) - payroll_period.standard_tax_exemption_amount = 52500 - payroll_period.save() + + income_tax_slab = frappe.new_doc("Income Tax Slab") + income_tax_slab.name = "Tax Slab: " + payroll_period.name + income_tax_slab.effective_from = effective_date or add_days(payroll_period.start_date, -2) + + if allow_tax_exemption: + income_tax_slab.allow_tax_exemption = 1 + income_tax_slab.standard_tax_exemption_amount = 50000 + + for item in slabs: + income_tax_slab.append("slabs", item) + + income_tax_slab.append("other_taxes_and_charges", { + "description": "cess", + "percent": 4 + }) + + income_tax_slab.save() + if not dont_submit: + income_tax_slab.submit() def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True): deducted_dates = [] diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js index 672be0c2fc2..2f2c815d70c 100755 --- a/erpnext/hr/doctype/salary_structure/salary_structure.js +++ b/erpnext/hr/doctype/salary_structure/salary_structure.js @@ -82,6 +82,7 @@ frappe.ui.form.on('Salary Structure', { {fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")}, {fieldname:'base_variable', fieldtype:'Section Break'}, {fieldname:'from_date', fieldtype:'Date', label: __('From Date'), "reqd": 1}, + {fieldname:'income_tax_slab', fieldtype:'Link', label: __('Income Tax Slab'), options: 'Income Tax Slab'}, {fieldname:'base_col_br', fieldtype:'Column Break'}, {fieldname:'base', fieldtype:'Currency', label: __('Base')}, {fieldname:'variable', fieldtype:'Currency', label: __('Variable')} diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py index 568277f8a73..df76458fe02 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/salary_structure.py @@ -16,6 +16,7 @@ class SalaryStructure(Document): self.validate_amount() self.strip_condition_and_formula_fields() self.validate_max_benefits_with_flexi() + self.validate_component_based_on_tax_slab() def set_missing_values(self): overwritten_fields = ["depends_on_payment_days", "variable_based_on_taxable_salary", "is_tax_applicable", "is_flexible_benefit"] @@ -34,6 +35,12 @@ class SalaryStructure(Document): for fieldname in overwritten_fields_if_missing: d.set(fieldname, component_default_value.get(fieldname)) + def validate_component_based_on_tax_slab(self): + for row in self.deductions: + if row.variable_based_on_taxable_salary and (row.amount or row.formula): + frappe.throw(_("Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary") + .format(row.idx, row.salary_component)) + def validate_amount(self): if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet: frappe.throw(_("Net pay cannot be negative")) @@ -82,21 +89,23 @@ class SalaryStructure(Document): @frappe.whitelist() def assign_salary_structure(self, company=None, grade=None, department=None, designation=None,employee=None, - from_date=None, base=None,variable=None): + from_date=None, base=None, variable=None, income_tax_slab=None): employees = self.get_employees(company= company, grade= grade,department= department,designation= designation,name=employee) if employees: if len(employees) > 20: frappe.enqueue(assign_salary_structure_for_employees, timeout=600, - employees=employees, salary_structure=self,from_date=from_date, base=base,variable=variable) + employees=employees, salary_structure=self,from_date=from_date, + base=base, variable=variable, income_tax_slab=income_tax_slab) else: - assign_salary_structure_for_employees(employees, self, from_date=from_date, base=base,variable=variable) + assign_salary_structure_for_employees(employees, self, from_date=from_date, + base=base, variable=variable, income_tax_slab=income_tax_slab) else: frappe.msgprint(_("No Employee Found")) -def assign_salary_structure_for_employees(employees, salary_structure, from_date=None, base=None,variable=None): +def assign_salary_structure_for_employees(employees, salary_structure, from_date=None, base=None, variable=None, income_tax_slab=None): salary_structures_assignments = [] existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date) count=0 @@ -105,7 +114,8 @@ def assign_salary_structure_for_employees(employees, salary_structure, from_date continue count +=1 - salary_structures_assignment = create_salary_structures_assignment(employee, salary_structure, from_date, base, variable) + salary_structures_assignment = create_salary_structures_assignment(employee, + salary_structure, from_date, base, variable, income_tax_slab) salary_structures_assignments.append(salary_structures_assignment) frappe.publish_progress(count*100/len(set(employees) - set(existing_assignments_for)), title = _("Assigning Structures...")) @@ -113,7 +123,7 @@ def assign_salary_structure_for_employees(employees, salary_structure, from_date frappe.msgprint(_("Structures have been assigned successfully")) -def create_salary_structures_assignment(employee, salary_structure, from_date, base, variable): +def create_salary_structures_assignment(employee, salary_structure, from_date, base, variable, income_tax_slab=None): assignment = frappe.new_doc("Salary Structure Assignment") assignment.employee = employee assignment.salary_structure = salary_structure.name @@ -121,6 +131,7 @@ def create_salary_structures_assignment(employee, salary_structure, from_date, b assignment.from_date = from_date assignment.base = base assignment.variable = variable + assignment.income_tax_slab = income_tax_slab assignment.save(ignore_permissions = True) assignment.submit() return assignment.name diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index f67a189ee71..3e55a1a068d 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -9,8 +9,9 @@ from frappe.utils.make_random import get_random from frappe.utils import nowdate, add_days, add_years, getdate, add_months from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.hr.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\ - make_deduction_salary_component, make_employee_salary_slip + make_deduction_salary_component, make_employee_salary_slip, create_tax_slab from erpnext.hr.doctype.employee.test_employee import make_employee +from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_payroll_period test_dependencies = ["Fiscal Year"] @@ -70,10 +71,8 @@ class TestSalaryStructure(unittest.TestCase): self.assertEqual(sal_slip.get("earnings")[1].amount, 3000) self.assertEqual(sal_slip.get("earnings")[2].amount, 25000) self.assertEqual(sal_slip.get("gross_pay"), 78000) - self.assertEqual(sal_slip.get("deductions")[0].amount, 5000) - self.assertEqual(sal_slip.get("deductions")[1].amount, 5000) - self.assertEqual(sal_slip.get("total_deduction"), 10000) - self.assertEqual(sal_slip.get("net_pay"), 68000) + self.assertEqual(sal_slip.get("deductions")[0].amount, 200) + self.assertEqual(sal_slip.get("net_pay"), 78000 - sal_slip.get("total_deduction")) def test_whitespaces_in_formula_conditions_fields(self): salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True) @@ -111,6 +110,7 @@ class TestSalaryStructure(unittest.TestCase): def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None, test_tax=False): if test_tax: frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure)) + if not frappe.db.exists('Salary Structure', salary_structure): details = { "doctype": "Salary Structure", @@ -123,7 +123,8 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do } if other_details and isinstance(other_details, dict): details.update(other_details) - salary_structure_doc = frappe.get_doc(details).insert() + salary_structure_doc = frappe.get_doc(details) + salary_structure_doc.insert() if not dont_submit: salary_structure_doc.submit() else: @@ -138,13 +139,18 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do def create_salary_structure_assignment(employee, salary_structure, from_date=None): if frappe.db.exists("Salary Structure Assignment", {"employee": employee}): frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""",(employee)) + + payroll_period = create_payroll_period() + create_tax_slab(payroll_period, allow_tax_exemption=True) + salary_structure_assignment = frappe.new_doc("Salary Structure Assignment") salary_structure_assignment.employee = employee salary_structure_assignment.base = 50000 salary_structure_assignment.variable = 5000 - salary_structure_assignment.from_date = from_date or add_months(nowdate(), -1) + salary_structure_assignment.from_date = from_date or add_days(nowdate(), -1) salary_structure_assignment.salary_structure = salary_structure salary_structure_assignment.company = erpnext.get_default_company() salary_structure_assignment.save(ignore_permissions=True) + salary_structure_assignment.income_tax_slab = "Tax Slab: _Test Payroll Period" salary_structure_assignment.submit() return salary_structure_assignment diff --git a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.js b/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.js index 56a05e04956..818e853154d 100644 --- a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.js +++ b/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.js @@ -20,6 +20,16 @@ frappe.ui.form.on('Salary Structure Assignment', { } } }); + + frm.set_query("income_tax_slab", function() { + return { + filters: { + company: frm.doc.company, + docstatus: 1, + disabled: 0 + } + } + }); }, employee: function(frm) { if(frm.doc.employee){ diff --git a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json index 136eb0f8053..0098aa8ec80 100644 --- a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json +++ b/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json @@ -10,11 +10,12 @@ "employee", "employee_name", "department", - "designation", + "company", "column_break_6", + "designation", "salary_structure", "from_date", - "company", + "income_tax_slab", "section_break_7", "base", "column_break_9", @@ -113,11 +114,17 @@ "options": "Salary Structure Assignment", "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "income_tax_slab", + "fieldtype": "Link", + "label": "Income Tax Slab", + "options": "Income Tax Slab" } ], "is_submittable": 1, "links": [], - "modified": "2019-12-31 17:05:28.637510", + "modified": "2020-04-25 18:24:23.617088", "modified_by": "Administrator", "module": "HR", "name": "Salary Structure Assignment", diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 4f5653dc67e..5c95e000f9c 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -9,6 +9,8 @@ from frappe.model.document import Document from frappe.desk.form import assign_to from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee +class DuplicateDeclarationError(frappe.ValidationError): pass + class EmployeeBoardingController(Document): ''' Create the project and the task for the boarding process @@ -226,6 +228,17 @@ def get_employee_leave_policy(employee): else: frappe.throw(_("Please set leave policy for employee {0} in Employee / Grade record").format(employee)) +def validate_duplicate_exemption_for_payroll_period(doctype, docname, payroll_period, employee): + existing_record = frappe.db.exists(doctype, { + "payroll_period": payroll_period, + "employee": employee, + 'docstatus': ['<', 2], + 'name': ['!=', docname] + }) + if existing_record: + frappe.throw(_("{0} already exists for employee {1} and period {2}") + .format(doctype, employee, payroll_period), DuplicateDeclarationError) + def validate_tax_declaration(declarations): subcategories = [] for d in declarations: diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 65c6af8b992..4a1949f2987 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -657,3 +657,4 @@ erpnext.patches.v12_0.recalculate_requested_qty_in_bin erpnext.patches.v12_0.rename_mws_settings_fields erpnext.patches.v12_0.set_correct_status_for_expense_claim erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse +erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 diff --git a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py index 121a20288c3..d7936110edb 100644 --- a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py +++ b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py @@ -6,4 +6,5 @@ def execute(): if not company: return + frappe.reload_doc("regional", "doctype", "lower_deduction_certificate") add_permissions() \ No newline at end of file diff --git a/erpnext/patches/v11_0/set_salary_component_properties.py b/erpnext/patches/v11_0/set_salary_component_properties.py index fa3605ba5f1..83fb53d2a73 100644 --- a/erpnext/patches/v11_0/set_salary_component_properties.py +++ b/erpnext/patches/v11_0/set_salary_component_properties.py @@ -5,8 +5,7 @@ def execute(): frappe.reload_doc('hr', 'doctype', 'salary_detail') frappe.reload_doc('hr', 'doctype', 'salary_component') - frappe.db.sql("update `tabSalary Component` set is_payable=1, is_tax_applicable=1 where type='Earning'") - frappe.db.sql("update `tabSalary Component` set is_payable=0 where type='Deduction'") + frappe.db.sql("update `tabSalary Component` set is_tax_applicable=1 where type='Earning'") frappe.db.sql("""update `tabSalary Component` set variable_based_on_taxable_salary=1 where type='Deduction' and name in ('TDS', 'Tax Deducted at Source')""") diff --git a/erpnext/patches/v13_0/__init__.py b/erpnext/patches/v13_0/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py new file mode 100644 index 00000000000..179be2cfde5 --- /dev/null +++ b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py @@ -0,0 +1,99 @@ +# 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(): + if not frappe.db.table_exists("Payroll Period"): + return + + for doctype in ("income_tax_slab", "salary_structure_assignment", "employee_other_income", "income_tax_slab_other_charges"): + frappe.reload_doc("hr", "doctype", doctype) + + + for company in frappe.get_all("Company"): + payroll_periods = frappe.db.sql(""" + SELECT + name, start_date, end_date, standard_tax_exemption_amount + FROM + `tabPayroll Period` + WHERE company=%s + ORDER BY start_date DESC + """, company.name, as_dict = 1) + + for i, period in enumerate(payroll_periods): + income_tax_slab = frappe.new_doc("Income Tax Slab") + income_tax_slab.name = "Tax Slab:" + period.name + + if i == 0: + income_tax_slab.disabled = 0 + else: + income_tax_slab.disabled = 1 + + income_tax_slab.effective_from = period.start_date + income_tax_slab.company = company.name + income_tax_slab.allow_tax_exemption = 1 + income_tax_slab.standard_tax_exemption_amount = period.standard_tax_exemption_amount + + income_tax_slab.flags.ignore_mandatory = True + income_tax_slab.submit() + + frappe.db.sql( + """ UPDATE `tabTaxable Salary Slab` + SET parent = %s , parentfield = 'slabs' , parenttype = "Income Tax Slab" + WHERE parent = %s + """, (income_tax_slab.name, period.name), as_dict = 1) + + if i == 0: + frappe.db.sql(""" + UPDATE + `tabSalary Structure Assignment` + set + income_tax_slab = %s + where + company = %s + and from_date >= %s + and docstatus < 2 + """, (income_tax_slab.name, company.name, period.start_date)) + + # move other incomes to separate document + migrated = [] + proofs = frappe.get_all("Employee Tax Exemption Proof Submission", + filters = {'docstatus': 1}, + fields =['payroll_period', 'employee', 'company', 'income_from_other_sources'] + ) + for proof in proofs: + if proof.income_from_other_sources: + employee_other_income = frappe.new_doc("Employee Other Income") + employee_other_income.employee = proof.employee + employee_other_income.payroll_period = proof.payroll_period + employee_other_income.company = proof.company + employee_other_income.amount = proof.income_from_other_sources + + try: + employee_other_income.submit() + migrated.append([proof.employee, proof.payroll_period]) + except: + pass + + declerations = frappe.get_all("Employee Tax Exemption Declaration", + filters = {'docstatus': 1}, + fields =['payroll_period', 'employee', 'company', 'income_from_other_sources'] + ) + + for declaration in declerations: + if declaration.income_from_other_sources \ + and [declaration.employee, declaration.payroll_period] not in migrated: + employee_other_income = frappe.new_doc("Employee Other Income") + employee_other_income.employee = declaration.employee + employee_other_income.payroll_period = declaration.payroll_period + employee_other_income.company = declaration.company + employee_other_income.amount = declaration.income_from_other_sources + + try: + employee_other_income.submit() + except: + pass diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 1957e7fecca..da43cc65553 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -530,12 +530,18 @@ def make_fixtures(company=None): def set_salary_components(docs): docs.extend([ - {'doctype': 'Salary Component', 'salary_component': 'Professional Tax', 'description': 'Professional Tax', 'type': 'Deduction'}, - {'doctype': 'Salary Component', 'salary_component': 'Provident Fund', 'description': 'Provident fund', 'type': 'Deduction'}, - {'doctype': 'Salary Component', 'salary_component': 'House Rent Allowance', 'description': 'House Rent Allowance', 'type': 'Earning'}, - {'doctype': 'Salary Component', 'salary_component': 'Basic', 'description': 'Basic', 'type': 'Earning'}, - {'doctype': 'Salary Component', 'salary_component': 'Arrear', 'description': 'Arrear', 'type': 'Earning'}, - {'doctype': 'Salary Component', 'salary_component': 'Leave Encashment', 'description': 'Leave Encashment', 'type': 'Earning'} + {'doctype': 'Salary Component', 'salary_component': 'Professional Tax', + 'description': 'Professional Tax', 'type': 'Deduction', 'exempted_from_income_tax': 1}, + {'doctype': 'Salary Component', 'salary_component': 'Provident Fund', + 'description': 'Provident fund', 'type': 'Deduction', 'is_tax_applicable': 1}, + {'doctype': 'Salary Component', 'salary_component': 'House Rent Allowance', + 'description': 'House Rent Allowance', 'type': 'Earning', 'is_tax_applicable': 1}, + {'doctype': 'Salary Component', 'salary_component': 'Basic', + 'description': 'Basic', 'type': 'Earning', 'is_tax_applicable': 1}, + {'doctype': 'Salary Component', 'salary_component': 'Arrear', + 'description': 'Arrear', 'type': 'Earning', 'is_tax_applicable': 1}, + {'doctype': 'Salary Component', 'salary_component': 'Leave Encashment', + 'description': 'Leave Encashment', 'type': 'Earning', 'is_tax_applicable': 1} ]) def set_tax_withholding_category(company): From 1c03d154ce78caa1dc48c5e0f2c0e005ce5dadcc Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 7 May 2020 12:11:23 +0530 Subject: [PATCH 452/929] fix(item): patch to rename duplicate item_code values to name (#21619) --- erpnext/patches.txt | 1 + .../patches/v11_0/rename_duplicate_item_code_values.py | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 erpnext/patches/v11_0/rename_duplicate_item_code_values.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4a1949f2987..89b5d54c199 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -496,6 +496,7 @@ erpnext.patches.v10_0.rename_offer_letter_to_job_offer execute:frappe.delete_doc('DocType', 'Production Planning Tool', ignore_missing=True) erpnext.patches.v10_0.migrate_daily_work_summary_settings_to_daily_work_summary_group # 24-12-2018 erpnext.patches.v10_0.add_default_cash_flow_mappers +erpnext.patches.v11_0.rename_duplicate_item_code_values erpnext.patches.v11_0.make_quality_inspection_template erpnext.patches.v10_0.update_status_for_multiple_source_in_po erpnext.patches.v10_0.set_auto_created_serial_no_in_stock_entry diff --git a/erpnext/patches/v11_0/rename_duplicate_item_code_values.py b/erpnext/patches/v11_0/rename_duplicate_item_code_values.py new file mode 100644 index 00000000000..00ab562c353 --- /dev/null +++ b/erpnext/patches/v11_0/rename_duplicate_item_code_values.py @@ -0,0 +1,8 @@ +import frappe + +def execute(): + items = [] + items = frappe.db.sql("""select item_code from `tabItem` group by item_code having count(*) > 1""", as_dict=True) + if items: + for item in items: + frappe.db.sql("""update `tabItem` set item_code=name where item_code = %s""", (item.item_code)) From 6d4f451d0dc6201d9deaf1c303365c09f1fc38ac Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Tue, 5 May 2020 20:07:48 +0530 Subject: [PATCH 453/929] fix(patch): reload Expense Claim doctype --- .../patches/v12_0/set_correct_status_for_expense_claim.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py b/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py index 1fefc90680e..c3a87fc5ca7 100644 --- a/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py +++ b/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py @@ -4,8 +4,11 @@ import frappe def execute(): + + frappe.reload_doc("hr", "doctype", "expense_claim") + frappe.db.sql(""" update `tabExpense Claim` set status = 'Paid' where total_advance_amount + total_amount_reimbursed = total_sanctioned_amount + total_taxes_and_charges - """) \ No newline at end of file + """) From 9fa952d10e26c9749127d7896eda6311c0f59ed6 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Mon, 4 May 2020 19:23:26 +0530 Subject: [PATCH 454/929] fix(patch): Reload GSTR 3B report --- erpnext/patches/v11_0/add_permissions_in_gst_settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py index d7936110edb..83b2a4cc09e 100644 --- a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py +++ b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py @@ -7,4 +7,5 @@ def execute(): return frappe.reload_doc("regional", "doctype", "lower_deduction_certificate") - add_permissions() \ No newline at end of file + frappe.reload_doc("regional", "doctype", "gstr_3b_report") + add_permissions() From 0b6ef55e78dfaaa18b3016b6eefce3c0165c1a53 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Thu, 7 May 2020 19:51:46 +0530 Subject: [PATCH 455/929] renaming LMS to Learning Management System --- .../education_settings/education_settings.json | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/education/doctype/education_settings/education_settings.json b/erpnext/education/doctype/education_settings/education_settings.json index 967a030fd2a..8ba189e93a1 100644 --- a/erpnext/education/doctype/education_settings/education_settings.json +++ b/erpnext/education/doctype/education_settings/education_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2017-04-05 13:33:04.519313", "doctype": "DocType", "editable_grid": 1, @@ -42,12 +43,14 @@ "fieldtype": "Column Break" }, { + "default": "0", "description": "For Batch based Student Group, the Student Batch will be validated for every Student from the Program Enrollment.", "fieldname": "validate_batch", "fieldtype": "Check", "label": "Validate Batch for Students in Student Group" }, { + "default": "0", "description": "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.", "fieldname": "validate_course", "fieldtype": "Check", @@ -74,13 +77,13 @@ { "fieldname": "web_academy_settings_section", "fieldtype": "Section Break", - "label": "LMS Settings" + "label": "Learning Management System Settings" }, { "depends_on": "eval: doc.enable_lms", "fieldname": "portal_title", "fieldtype": "Data", - "label": "LMS Title" + "label": "Learning Management System Title" }, { "depends_on": "eval: doc.enable_lms", @@ -89,9 +92,10 @@ "label": "Description" }, { + "default": "0", "fieldname": "enable_lms", "fieldtype": "Check", - "label": "Enable LMS" + "label": "Enable Learning Management System" }, { "default": "0", @@ -102,7 +106,8 @@ } ], "issingle": 1, - "modified": "2019-05-13 18:36:13.127563", + "links": [], + "modified": "2020-05-07 19:50:22.430576", "modified_by": "Administrator", "module": "Education", "name": "Education Settings", @@ -141,4 +146,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file From 6844dd75dabe9ef6917401a4ee2373b0b644efef Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 7 May 2020 23:40:17 +0530 Subject: [PATCH 456/929] fix: delete old appointment analytics tree grid report --- erpnext/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 580126d401e..69291af9cd2 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -662,3 +662,4 @@ erpnext.patches.v12_0.set_updated_purpose_in_pick_list erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 +execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") \ No newline at end of file From 56a3b9abc4caedc4bde3a8b176d4d6b89f82f263 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Fri, 8 May 2020 02:25:09 +0530 Subject: [PATCH 457/929] Payment Order not allowing to create Payment Entry --- erpnext/accounts/doctype/payment_order/payment_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_order/payment_order.py b/erpnext/accounts/doctype/payment_order/payment_order.py index 3f3174a69b6..7ecdc41d034 100644 --- a/erpnext/accounts/doctype/payment_order/payment_order.py +++ b/erpnext/accounts/doctype/payment_order/payment_order.py @@ -80,7 +80,7 @@ def make_journal_entry(doc, supplier, mode_of_payment=None): paid_amt += d.amount je.append('accounts', { - 'account': doc.references[0].account, + 'account': doc.account, 'credit_in_account_currency': paid_amt }) From 923fcc77387aa0f1c17e56cdd881690bc3038007 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 8 May 2020 15:14:39 +0530 Subject: [PATCH 458/929] chore: Change log --- erpnext/change_log/v12/v12_8_0.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 erpnext/change_log/v12/v12_8_0.md diff --git a/erpnext/change_log/v12/v12_8_0.md b/erpnext/change_log/v12/v12_8_0.md new file mode 100644 index 00000000000..47081083ec6 --- /dev/null +++ b/erpnext/change_log/v12/v12_8_0.md @@ -0,0 +1,11 @@ +## ERPNext v12.7.0 Release Note + +#### Income Tax Slab +Introduced a brand new **[Income Tax Slab](http://docs.local:8000/docs/user/manual/en/human-resources/income-tax-slab)** document to define individial's income tax rates based on different taxable income slab directed by the Government. +Using this document, Indian users will now be able to define income tax slabs for both old (2019) and new tax regime (2020). + +#### Salary Component Exempted from Income Tax +A new checkbox has been introduced in Salary Component. If checked, the full amount will be deducted from taxable income before calculating income tax without any declaration or proof submission. For example, Professional Tax in India is deducted from taxable income without any document proof. + +#### Employee Other Income +Employee Other Income is now a new document to declare other income of an employee from other sources. Previously, it was part of the Employee Tax Exemption Declaration document. \ No newline at end of file From 664f536e9b19c02ab16bc8b10d51209af59ac0b8 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Fri, 8 May 2020 15:24:23 +0530 Subject: [PATCH 459/929] chore: correct version for release note --- erpnext/change_log/v12/v12_8_0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/change_log/v12/v12_8_0.md b/erpnext/change_log/v12/v12_8_0.md index 47081083ec6..1417f065681 100644 --- a/erpnext/change_log/v12/v12_8_0.md +++ b/erpnext/change_log/v12/v12_8_0.md @@ -1,4 +1,4 @@ -## ERPNext v12.7.0 Release Note +## ERPNext v12.8.0 Release Note #### Income Tax Slab Introduced a brand new **[Income Tax Slab](http://docs.local:8000/docs/user/manual/en/human-resources/income-tax-slab)** document to define individial's income tax rates based on different taxable income slab directed by the Government. @@ -8,4 +8,4 @@ Using this document, Indian users will now be able to define income tax slabs fo A new checkbox has been introduced in Salary Component. If checked, the full amount will be deducted from taxable income before calculating income tax without any declaration or proof submission. For example, Professional Tax in India is deducted from taxable income without any document proof. #### Employee Other Income -Employee Other Income is now a new document to declare other income of an employee from other sources. Previously, it was part of the Employee Tax Exemption Declaration document. \ No newline at end of file +Employee Other Income is now a new document to declare other income of an employee from other sources. Previously, it was part of the Employee Tax Exemption Declaration document. From 41ff2cb4a7e1cbb8650b4efeb5aba02ed733f5f4 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Fri, 8 May 2020 15:30:46 +0530 Subject: [PATCH 460/929] chore: correct link to documentation --- erpnext/change_log/v12/v12_8_0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/change_log/v12/v12_8_0.md b/erpnext/change_log/v12/v12_8_0.md index 1417f065681..87f46fe2c84 100644 --- a/erpnext/change_log/v12/v12_8_0.md +++ b/erpnext/change_log/v12/v12_8_0.md @@ -1,7 +1,7 @@ ## ERPNext v12.8.0 Release Note #### Income Tax Slab -Introduced a brand new **[Income Tax Slab](http://docs.local:8000/docs/user/manual/en/human-resources/income-tax-slab)** document to define individial's income tax rates based on different taxable income slab directed by the Government. +Introduced a brand new **[Income Tax Slab](https://docs.erpnext.com/docs/user/manual/en/human-resources/income-tax-slab)** document to define individial's income tax rates based on different taxable income slab directed by the Government. Using this document, Indian users will now be able to define income tax slabs for both old (2019) and new tax regime (2020). #### Salary Component Exempted from Income Tax From 5afcc9c1850ef79951d507f27da47571f27060a4 Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Fri, 8 May 2020 16:02:18 +0550 Subject: [PATCH 461/929] bumped to version 12.8.0 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 2aea9a3cf5b..9b972e7561e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.7.1' +__version__ = '12.8.0' def get_default_company(user=None): '''Get default company for user''' From 231f65ff59e214516003f654978afa37ba227a85 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 8 May 2020 16:30:46 +0530 Subject: [PATCH 462/929] fix: Item Barcode stays the same after updation. --- 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 3e37a1d4899..d7edd3ab51f 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -572,6 +572,13 @@ class Item(WebsiteGenerator): frappe.throw(_("Barcode {0} is not a valid {1} code").format( item_barcode.barcode, item_barcode.barcode_type), InvalidBarcode) + if item_barcode.barcode != item_barcode.name: + # if barcode is getting updated , the row name has to reset. + # Delete previous old row doc and re-enter row as if new to reset name in db. + item_barcode.set("__islocal", True) + item_barcode.name = None + frappe.delete_doc("Item Barcode", item_barcode.name) + def validate_warehouse_for_reorder(self): '''Validate Reorder level table for duplicate and conditional mandatory''' warehouse = [] From c739c2d355bf9175798dd8db12bd3fc71702abd7 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Fri, 8 May 2020 17:38:14 +0530 Subject: [PATCH 463/929] Appending Email and Phone in Child Table --- erpnext/selling/doctype/customer/customer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 3da0eb86d1d..ad5bfa835db 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -164,9 +164,11 @@ class Customer(TransactionBase): contact.phone = lead.phone contact.mobile_no = lead.mobile_no contact.is_primary_contact = 1 - contact.append('email_ids', dict(email_id=lead.email_id, is_primary=1)) - contact.append('phone_nos', dict(phone=lead.mobile_no, is_primary_mobile_no=1)) contact.append('links', dict(link_doctype='Customer', link_name=self.name)) + if lead.email_id: + contact.append('email_ids', dict(email_id=lead.email_id, is_primary=1)) + if lead.mobile_no: + contact.append('phone_nos', dict(phone=lead.mobile_no, is_primary_mobile_no=1)) contact.flags.ignore_permissions = self.flags.ignore_permissions contact.autoname() if not frappe.db.exists("Contact", contact.name): From 27b2988938c628e803a69897cde5cc90ed69151c Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sat, 9 May 2020 16:44:36 +0530 Subject: [PATCH 464/929] Assessment Plan not getting created --- .../assessment_plan/assessment_plan.json | 702 ++---------------- 1 file changed, 54 insertions(+), 648 deletions(-) diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan.json b/erpnext/education/doctype/assessment_plan/assessment_plan.json index bc3946440c3..c7df672e71b 100644 --- a/erpnext/education/doctype/assessment_plan/assessment_plan.json +++ b/erpnext/education/doctype/assessment_plan/assessment_plan.json @@ -1,790 +1,207 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, - "allow_rename": 0, "autoname": "EDU-ASP-.YYYY.-.#####", - "beta": 0, "creation": "2015-11-12 16:34:34.658092", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "student_group", + "assessment_name", + "assessment_group", + "grading_scale", + "column_break_2", + "course", + "program", + "academic_year", + "academic_term", + "section_break_5", + "schedule_date", + "room", + "examiner", + "examiner_name", + "column_break_4", + "from_time", + "to_time", + "supervisor", + "supervisor_name", + "section_break_20", + "maximum_assessment_score", + "assessment_criteria", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "student_group", "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": "Student Group", - "length": 0, - "no_copy": 0, "options": "Student Group", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "assessment_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": "Assessment 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, - "translatable": 0, - "unique": 0 + "label": "Assessment Name" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "assessment_group", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Assessment Group", - "length": 0, - "no_copy": 0, "options": "Assessment Group", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "course.default_grading_scale", + "fetch_if_empty": 1, "fieldname": "grading_scale", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Grading Scale", - "length": 0, - "no_copy": 0, "options": "Grading Scale", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, "fetch_from": "student_group.course", + "fetch_if_empty": 1, "fieldname": "course", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, "in_standard_filter": 1, "label": "Course", - "length": 0, - "no_copy": 0, "options": "Course", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "student_group.program", "fieldname": "program", "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": "Program", - "length": 0, - "no_copy": 0, - "options": "Program", - "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": "Program" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "student_group.academic_year", "fieldname": "academic_year", "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": "Academic Year", - "length": 0, - "no_copy": 0, - "options": "Academic Year", - "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": "Academic Year" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "student_group.academic_term", "fieldname": "academic_term", "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": "Academic Term", - "length": 0, - "no_copy": 0, - "options": "Academic Term", - "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": "Academic Term" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "", "fieldname": "section_break_5", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Schedule", - "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": "Schedule" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Today", "fieldname": "schedule_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": "Schedule Date", - "length": 0, "no_copy": 1, - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "room", "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": "Room", - "length": 0, - "no_copy": 0, - "options": "Room", - "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": "Room" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "examiner", "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": "Examiner", - "length": 0, - "no_copy": 0, - "options": "Instructor", - "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": "Instructor" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "examiner.instructor_name", "fieldname": "examiner_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": "Examiner 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 + "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_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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "from_time", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "From Time", - "length": 0, "no_copy": 1, - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "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": 1, - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "supervisor", "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": "Supervisor", - "length": 0, - "no_copy": 0, - "options": "Instructor", - "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": "Instructor" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "supervisor.instructor_name", "fieldname": "supervisor_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": "Supervisor 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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_20", "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": "Evaluate", - "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": "Evaluate" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "maximum_assessment_score", "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": "Maximum Assessment Score", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "assessment_criteria", "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": "Assessment Criteria", - "length": 0, - "no_copy": 0, "options": "Assessment Plan Criteria", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "amended_from", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Amended From", - "length": 0, "no_copy": 1, "options": "Assessment Plan", - "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 + "read_only": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2018-08-30 00:48:03.475522", + "links": [], + "modified": "2020-05-09 16:44:04.313175", "modified_by": "Administrator", "module": "Education", "name": "Assessment Plan", - "name_case": "", "owner": "Administrator", "permissions": [ { @@ -794,28 +211,17 @@ "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Academics User", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Education", - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "title_field": "assessment_name", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "title_field": "assessment_name" } \ No newline at end of file From d837b6cedce7016560b38166ef3bcc6ef3b039fc Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Thu, 7 May 2020 17:49:32 +0800 Subject: [PATCH 465/929] style: Improve formatting This commit improves indentations and makes sql queries more readable. --- .../budget_variance_report.py | 418 ++++++++++++------ 1 file changed, 284 insertions(+), 134 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 39e218bfad2..f286a457576 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -2,187 +2,337 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals +from six import iteritems + import frappe from frappe import _ from frappe.utils import flt from frappe.utils import formatdate + from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges -from six import iteritems -from pprint import pprint + def execute(filters=None): - if not filters: filters = {} + if not filters: + filters = {} - columns = get_columns(filters) - if filters.get("budget_against_filter"): - dimensions = filters.get("budget_against_filter") - else: - dimensions = get_cost_centers(filters) + columns = get_columns(filters) - period_month_ranges = get_period_month_ranges(filters["period"], filters["from_fiscal_year"]) - cam_map = get_dimension_account_month_map(filters) + if filters.get("budget_against_filter"): + dimensions = filters.get("budget_against_filter") + else: + dimensions = get_cost_centers(filters) - data = [] - for dimension in dimensions: - dimension_items = cam_map.get(dimension) - if dimension_items: - for account, monthwise_data in iteritems(dimension_items): - row = [dimension, account] - totals = [0, 0, 0] - for year in get_fiscal_years(filters): - last_total = 0 - for relevant_months in period_month_ranges: - period_data = [0, 0, 0] - for month in relevant_months: - if monthwise_data.get(year[0]): - month_data = monthwise_data.get(year[0]).get(month, {}) - for i, fieldname in enumerate(["target", "actual", "variance"]): - value = flt(month_data.get(fieldname)) - period_data[i] += value - totals[i] += value + period_month_ranges = get_period_month_ranges( + filters["period"], filters["from_fiscal_year"] + ) - period_data[0] += last_total + cam_map = get_dimension_account_month_map(filters) - if(filters.get("show_cumulative")): - last_total = period_data[0] - period_data[1] + data = [] + for dimension in dimensions: + dimension_items = cam_map.get(dimension) + if dimension_items: + for account, monthwise_data in iteritems(dimension_items): + row = [dimension, account] + totals = [0, 0, 0] + for year in get_fiscal_years(filters): + last_total = 0 + for relevant_months in period_month_ranges: + period_data = [0, 0, 0] + for month in relevant_months: + if monthwise_data.get(year[0]): + month_data = monthwise_data.get(year[0]).get(month, {}) + for i, fieldname in enumerate( + ["target", "actual", "variance"] + ): + value = flt(month_data.get(fieldname)) + period_data[i] += value + totals[i] += value - period_data[2] = period_data[0] - period_data[1] - row += period_data - totals[2] = totals[0] - totals[1] - if filters["period"] != "Yearly" : - row += totals - data.append(row) + period_data[0] += last_total + + if filters.get("show_cumulative"): + last_total = period_data[0] - period_data[1] + + period_data[2] = period_data[0] - period_data[1] + row += period_data + totals[2] = totals[0] - totals[1] + if filters["period"] != "Yearly": + row += totals + data.append(row) + + return columns, data - return columns, data def get_columns(filters): - columns = [_(filters.get("budget_against")) + ":Link/%s:150"%(filters.get("budget_against")), _("Account") + ":Link/Account:150"] + columns = [ + _(filters.get("budget_against")) + + ":Link/%s:150" % (filters.get("budget_against")), + _("Account") + ":Link/Account:150", + ] - group_months = False if filters["period"] == "Monthly" else True + group_months = False if filters["period"] == "Monthly" else True - fiscal_year = get_fiscal_years(filters) + fiscal_year = get_fiscal_years(filters) - for year in fiscal_year: - for from_date, to_date in get_period_date_ranges(filters["period"], year[0]): - if filters["period"] == "Yearly": - labels = [_("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), _("Variance ") + " " + str(year[0])] - for label in labels: - columns.append(label+":Float:150") - else: - for label in [_("Budget") + " (%s)" + " " + str(year[0]), _("Actual") + " (%s)" + " " + str(year[0]), _("Variance") + " (%s)" + " " + str(year[0])]: - if group_months: - label = label % (formatdate(from_date, format_string="MMM") + "-" + formatdate(to_date, format_string="MMM")) - else: - label = label % formatdate(from_date, format_string="MMM") + for year in fiscal_year: + for from_date, to_date in get_period_date_ranges(filters["period"], year[0]): + if filters["period"] == "Yearly": + labels = [ + _("Budget") + " " + str(year[0]), + _("Actual ") + " " + str(year[0]), + _("Variance ") + " " + str(year[0]), + ] + for label in labels: + columns.append(label + ":Float:150") + else: + for label in [ + _("Budget") + " (%s)" + " " + str(year[0]), + _("Actual") + " (%s)" + " " + str(year[0]), + _("Variance") + " (%s)" + " " + str(year[0]), + ]: + if group_months: + label = label % ( + formatdate(from_date, format_string="MMM") + + "-" + + formatdate(to_date, format_string="MMM") + ) + else: + label = label % formatdate(from_date, format_string="MMM") - columns.append(label+":Float:150") + columns.append(label + ":Float:150") + + if filters["period"] != "Yearly": + return columns + [ + _("Total Budget") + ":Float:150", + _("Total Actual") + ":Float:150", + _("Total Variance") + ":Float:150", + ] + else: + return columns - if filters["period"] != "Yearly" : - return columns + [_("Total Budget") + ":Float:150", _("Total Actual") + ":Float:150", - _("Total Variance") + ":Float:150"] - else: - return columns def get_cost_centers(filters): - cond = "and 1=1" - if filters.get("budget_against") == "Cost Center": - cond = "order by lft" + order_by = "" + if filters.get("budget_against") == "Cost Center": + order_by = "order by lft" - if filters.get("budget_against") in ["Cost Center", "Project"]: - return frappe.db.sql_list("""select name from `tab{tab}` where company=%s - {cond}""".format(tab=filters.get("budget_against"), cond=cond), filters.get("company")) - else: - return frappe.db.sql_list("""select name from `tab{tab}`""".format(tab=filters.get("budget_against"))) #nosec + if filters.get("budget_against") in ["Cost Center", "Project"]: + return frappe.db.sql_list( + """ + select + name + from + `tab{tab}` + where + company = %s + {order_by}; + """.format( + tab=filters.get("budget_against"), order_by=order_by + ), + filters.get("company"), + ) + else: + return frappe.db.sql_list( + """ + select + name + from + `tab{tab}` + """.format( + tab=filters.get("budget_against") + ) + ) # nosec -#Get dimension & target details + +# Get dimension & target details def get_dimension_target_details(filters): - cond = "" - if filters.get("budget_against_filter"): - cond += " and b.{budget_against} in (%s)".format(budget_against = \ - frappe.scrub(filters.get('budget_against'))) % ', '.join(['%s']* len(filters.get('budget_against_filter'))) + budget_against = frappe.scrub(filters.get("budget_against")) - return frappe.db.sql(""" - select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year - from `tabBudget` b, `tabBudget Account` ba - where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s - and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year - """.format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond), - tuple([filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company] + filters.get('budget_against_filter')), - as_dict=True) + cond = "" + if filters.get("budget_against_filter"): + cond += """ + and + b.{budget_against} in ( + %s + ) + """.format( + budget_against=budget_against + ) % ", ".join( + ["%s"] * len(filters.get("budget_against_filter")) + ) + + return frappe.db.sql( + """ + select + b.{budget_against} as name + from + `tabBudget` b + where + b.docstatus = 1 + and b.fiscal_year between %s and %s + and b.budget_against = %s + and b.company = %s + {cond} + order by + b.fiscal_year + """.format( + budget_against=budget_against, cond=cond + ), + tuple( + [ + filters.from_fiscal_year, + filters.to_fiscal_year, + filters.budget_against, + filters.company, + ] + + filters.get("budget_against_filter") + ), + as_dict=True, + ) -#Get target distribution details of accounts of cost center +# Get target distribution details of accounts of cost center def get_target_distribution_details(filters): - target_details = {} - for d in frappe.db.sql("""select md.name, mdp.month, mdp.percentage_allocation - from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md - where mdp.parent=md.name and md.fiscal_year between %s and %s order by md.fiscal_year""",(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1): - target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation)) + target_details = {} + for d in frappe.db.sql( + """ + select + md.name, + mdp.month, + mdp.percentage_allocation + from + `tabMonthly Distribution Percentage` mdp, + `tabMonthly Distribution` md + where + mdp.parent = md.name + and md.fiscal_year between %s + and %s + order by md.fiscal_year + """, + (filters.from_fiscal_year, filters.to_fiscal_year), + as_dict=1, + ): + target_details.setdefault(d.name, {}).setdefault( + d.month, flt(d.percentage_allocation) + ) - return target_details + return target_details -#Get actual details from gl entry + +# Get actual details from gl entry def get_actual_details(name, filters): - cond = "1=1" - budget_against=filters.get("budget_against").replace(" ", "_").lower() + budget_against = frappe.scrub(filters.get("budget_against")) - if filters.get("budget_against") == "Cost Center": - cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) - cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt) + cond = "" + if filters.get("budget_against") == "Cost Center": + cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) + cond = """ + and lft >= '{lft}' + and rgt <= '{rgt}' + """.format( + lft=cc_lft, rgt=cc_rgt + ) - ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit,gl.fiscal_year, - MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against - from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b - where - b.name = ba.parent - and b.docstatus = 1 - and ba.account=gl.account - and b.{budget_against} = gl.{budget_against} - and gl.fiscal_year between %s and %s - and b.{budget_against}=%s - and exists(select name from `tab{tab}` where name=gl.{budget_against} and {cond}) group by gl.name order by gl.fiscal_year - """.format(tab = filters.budget_against, budget_against = budget_against, cond = cond,from_year=filters.from_fiscal_year,to_year=filters.to_fiscal_year), - (filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1) + ac_details = frappe.db.sql( + """ + select + gl.account, + gl.debit, + gl.credit, + gl.fiscal_year, + MONTHNAME(gl.posting_date) as month_name, + b.{budget_against} as budget_against + from + `tabGL Entry` gl, + `tabBudget Account` ba, + `tabBudget` b + where + b.name = ba.parent + and b.docstatus = 1 + and ba.account=gl.account + and b.{budget_against} = gl.{budget_against} + and gl.fiscal_year between %s and %s + and b.{budget_against} = %s + and exists( + select + name + from + `tab{tab}` + where + name = gl.{budget_against} + {cond} + ) + group by + gl.name + order by gl.fiscal_year + """.format( + tab=filters.budget_against, budget_against=budget_against, cond=cond + ), + (filters.from_fiscal_year, filters.to_fiscal_year, name), + as_dict=1, + ) - cc_actual_details = {} - for d in ac_details: - cc_actual_details.setdefault(d.account, []).append(d) + cc_actual_details = {} + for d in ac_details: + cc_actual_details.setdefault(d.account, []).append(d) + + return cc_actual_details - return cc_actual_details def get_dimension_account_month_map(filters): - import datetime - dimension_target_details = get_dimension_target_details(filters) - tdd = get_target_distribution_details(filters) + import datetime - cam_map = {} + dimension_target_details = get_dimension_target_details(filters) + tdd = get_target_distribution_details(filters) - for ccd in dimension_target_details: - actual_details = get_actual_details(ccd.budget_against, filters) + cam_map = {} - for month_id in range(1, 13): - month = datetime.date(2013, month_id, 1).strftime('%B') - cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(ccd.fiscal_year,{})\ - .setdefault(month, frappe._dict({ - "target": 0.0, "actual": 0.0 - })) + for ccd in dimension_target_details: + actual_details = get_actual_details(ccd.budget_against, filters) - tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month] - month_percentage = tdd.get(ccd.monthly_distribution, {}).get(month, 0) \ - if ccd.monthly_distribution else 100.0/12 + for month_id in range(1, 13): + month = datetime.date(2013, month_id, 1).strftime("%B") + cam_map.setdefault(ccd.budget_against, {}).setdefault( + ccd.account, {} + ).setdefault(ccd.fiscal_year, {}).setdefault( + month, frappe._dict({"target": 0.0, "actual": 0.0}) + ) - tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100 + tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month] + month_percentage = ( + tdd.get(ccd.monthly_distribution, {}).get(month, 0) + if ccd.monthly_distribution + else 100.0 / 12 + ) - for ad in actual_details.get(ccd.account, []): - if ad.month_name == month: - tav_dict.actual += flt(ad.debit) - flt(ad.credit) + tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100 + + for ad in actual_details.get(ccd.account, []): + if ad.month_name == month: + tav_dict.actual += flt(ad.debit) - flt(ad.credit) + + return cam_map - return cam_map def get_fiscal_years(filters): - fiscal_year = frappe.db.sql("""select name from `tabFiscal Year` where - name between %(from_fiscal_year)s and %(to_fiscal_year)s""", - {'from_fiscal_year': filters["from_fiscal_year"], 'to_fiscal_year': filters["to_fiscal_year"]}) + fiscal_year = frappe.db.sql( + """ + select + name + from + `tabFiscal Year` + where + name between %(from_fiscal_year)s and %(to_fiscal_year)s + order by + year + """, + { + "from_fiscal_year": filters["from_fiscal_year"], + "to_fiscal_year": filters["to_fiscal_year"], + }, + ) - return fiscal_year + return fiscal_year From 55640be627bbeff3e97ea4c9364d7d848ef61464 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Thu, 7 May 2020 18:39:47 +0800 Subject: [PATCH 466/929] fix: Fix Budget Variance Report This commit fixes a bug in Budget Variance Report where it combines the actual expense amounts across different fiscal years. This was fixed by updating the function and queries for computing the actual expense amounts. --- .../budget_variance_report.py | 172 +++++++++++++----- 1 file changed, 127 insertions(+), 45 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index f286a457576..1b110b0aacd 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -2,12 +2,12 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals +import datetime from six import iteritems import frappe from frappe import _ -from frappe.utils import flt -from frappe.utils import formatdate +from frappe.utils import flt, formatdate from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges @@ -126,8 +126,8 @@ def get_cost_centers(filters): from `tab{tab}` where - company = %s - {order_by}; + company=%s + {order_by} """.format( tab=filters.get("budget_against"), order_by=order_by ), @@ -153,11 +153,11 @@ def get_dimension_target_details(filters): cond = "" if filters.get("budget_against_filter"): cond += """ - and - b.{budget_against} in ( - %s - ) - """.format( + and + b.{budget_against} in ( + %s + ) + """.format( budget_against=budget_against ) % ", ".join( ["%s"] * len(filters.get("budget_against_filter")) @@ -165,7 +165,7 @@ def get_dimension_target_details(filters): return frappe.db.sql( """ - select + select distinct b.{budget_against} as name from `tabBudget` b @@ -198,19 +198,19 @@ def get_target_distribution_details(filters): target_details = {} for d in frappe.db.sql( """ - select - md.name, - mdp.month, - mdp.percentage_allocation - from - `tabMonthly Distribution Percentage` mdp, - `tabMonthly Distribution` md - where - mdp.parent = md.name - and md.fiscal_year between %s - and %s - order by md.fiscal_year - """, + select + md.name, + mdp.month, + mdp.percentage_allocation + from + `tabMonthly Distribution Percentage` mdp, + `tabMonthly Distribution` md + where + mdp.parent = md.name + and md.fiscal_year between %s + and %s + order by md.fiscal_year + """, (filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1, ): @@ -229,8 +229,8 @@ def get_actual_details(name, filters): if filters.get("budget_against") == "Cost Center": cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) cond = """ - and lft >= '{lft}' - and rgt <= '{rgt}' + and lft >= \'{lft}\' + and rgt <= \'{rgt}\' """.format( lft=cc_lft, rgt=cc_rgt ) @@ -282,37 +282,44 @@ def get_actual_details(name, filters): def get_dimension_account_month_map(filters): - import datetime - dimension_target_details = get_dimension_target_details(filters) tdd = get_target_distribution_details(filters) cam_map = {} for ccd in dimension_target_details: - actual_details = get_actual_details(ccd.budget_against, filters) + accounts = get_accounts(ccd.name, filters) + actual_details = get_actual_details(ccd.name, filters) - for month_id in range(1, 13): - month = datetime.date(2013, month_id, 1).strftime("%B") - cam_map.setdefault(ccd.budget_against, {}).setdefault( - ccd.account, {} - ).setdefault(ccd.fiscal_year, {}).setdefault( - month, frappe._dict({"target": 0.0, "actual": 0.0}) - ) + for year in get_fiscal_years(filters): + year = year[0] + monthly_distribution = get_monthly_distribution(ccd.name, year, filters) + for month_id in range(1, 13): + month = datetime.date(2013, month_id, 1).strftime( + "%B" + ) # Get month string - tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month] - month_percentage = ( - tdd.get(ccd.monthly_distribution, {}).get(month, 0) - if ccd.monthly_distribution - else 100.0 / 12 - ) + for account in accounts: + account = account[0] + cam_map.setdefault(ccd.name, {}).setdefault(account, {}).setdefault( + year, {} + ).setdefault(month, frappe._dict({"target": 0.0, "actual": 0.0})) - tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100 + tav_dict = cam_map[ccd.name][account][year][month] - for ad in actual_details.get(ccd.account, []): - if ad.month_name == month: - tav_dict.actual += flt(ad.debit) - flt(ad.credit) + month_percentage = ( + tdd.get(monthly_distribution, {}).get(month, 0) + if monthly_distribution + else 100.0 / 12 + ) + budget_amount = get_budget_amount(ccd.name, year, account, filters) + + tav_dict.target = flt(budget_amount) * month_percentage / 100 + + for ad in actual_details.get(account, []): + if ad.month_name == month and ad.fiscal_year == year: + tav_dict.actual += flt(ad.debit) - flt(ad.credit) return cam_map @@ -336,3 +343,78 @@ def get_fiscal_years(filters): ) return fiscal_year + + +def get_accounts(name, filters): + budget_against = frappe.scrub(filters.get("budget_against")) + + accounts = frappe.db.sql( + """ + select + distinct(ba.account) + from + `tabBudget Account` ba + join + `tabBudget` b + on b.name = ba.parent + where + b.docstatus = 1 + and b.fiscal_year between %s and %s + and b.{budget_against} = %s + order by + ba.account + """.format( + budget_against=budget_against + ), + (filters.from_fiscal_year, filters.to_fiscal_year, name), + ) + + return accounts + + +def get_monthly_distribution(name, year, filters): + budget_against = frappe.scrub(filters.get("budget_against")) + + monthly_distribution = frappe.db.sql( + """ + select + monthly_distribution + from + `tabBudget` + where + docstatus = 1 + and {budget_against} = %s + and fiscal_year = %s + """.format( + budget_against=budget_against + ), + (name, year), + ) + + return monthly_distribution[0][0] if monthly_distribution else None + + +def get_budget_amount(name, year, account, filters): + budget_against = frappe.scrub(filters.get("budget_against")) + + budget_amount = frappe.db.sql( + """ + select + ba.budget_amount + from + `tabBudget Account` ba + join + `tabBudget` b + on b.name = ba.parent + where + b.docstatus = 1 + and b.{budget_against} = %s + and b.fiscal_year = %s + and ba.account = %s + """.format( + budget_against=budget_against + ), + (name, year, account), + ) + + return budget_amount[0][0] if budget_amount else 0 From 861f2726a27d8397c889d68cd56d8721d05bfedf Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Sat, 9 May 2020 16:02:11 +0800 Subject: [PATCH 467/929] fix: Simplify get_dimension_account_month_map This commit updates get_dimension_account_month_map to no longer show the actual expense when there is no budget. This also removes the other functions and queries related to it. Spaces are also converted to tabs. --- .../budget_variance_report.py | 500 ++++++++---------- 1 file changed, 209 insertions(+), 291 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 1b110b0aacd..4d5892a9132 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -13,164 +13,165 @@ from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ def execute(filters=None): - if not filters: - filters = {} + if not filters: + filters = {} - columns = get_columns(filters) + columns = get_columns(filters) + if filters.get("budget_against_filter"): + dimensions = filters.get("budget_against_filter") + else: + dimensions = get_cost_centers(filters) - if filters.get("budget_against_filter"): - dimensions = filters.get("budget_against_filter") - else: - dimensions = get_cost_centers(filters) + period_month_ranges = get_period_month_ranges( + filters["period"], filters["from_fiscal_year"] + ) + cam_map = get_dimension_account_month_map(filters) - period_month_ranges = get_period_month_ranges( - filters["period"], filters["from_fiscal_year"] - ) + data = [] + for dimension in dimensions: + dimension_items = cam_map.get(dimension) + if dimension_items: + for account, monthwise_data in iteritems(dimension_items): + row = [dimension, account] + totals = [0, 0, 0] + for year in get_fiscal_years(filters): + last_total = 0 + for relevant_months in period_month_ranges: + period_data = [0, 0, 0] + for month in relevant_months: + if monthwise_data.get(year[0]): + month_data = monthwise_data.get(year[0]).get(month, {}) + for i, fieldname in enumerate( + ["target", "actual", "variance"] + ): + value = flt(month_data.get(fieldname)) + period_data[i] += value + totals[i] += value - cam_map = get_dimension_account_month_map(filters) + period_data[0] += last_total - data = [] - for dimension in dimensions: - dimension_items = cam_map.get(dimension) - if dimension_items: - for account, monthwise_data in iteritems(dimension_items): - row = [dimension, account] - totals = [0, 0, 0] - for year in get_fiscal_years(filters): - last_total = 0 - for relevant_months in period_month_ranges: - period_data = [0, 0, 0] - for month in relevant_months: - if monthwise_data.get(year[0]): - month_data = monthwise_data.get(year[0]).get(month, {}) - for i, fieldname in enumerate( - ["target", "actual", "variance"] - ): - value = flt(month_data.get(fieldname)) - period_data[i] += value - totals[i] += value + if filters.get("show_cumulative"): + last_total = period_data[0] - period_data[1] - period_data[0] += last_total + period_data[2] = period_data[0] - period_data[1] + row += period_data + totals[2] = totals[0] - totals[1] + if filters["period"] != "Yearly": + row += totals + data.append(row) - if filters.get("show_cumulative"): - last_total = period_data[0] - period_data[1] - - period_data[2] = period_data[0] - period_data[1] - row += period_data - totals[2] = totals[0] - totals[1] - if filters["period"] != "Yearly": - row += totals - data.append(row) - - return columns, data + return columns, data def get_columns(filters): - columns = [ - _(filters.get("budget_against")) - + ":Link/%s:150" % (filters.get("budget_against")), - _("Account") + ":Link/Account:150", - ] + columns = [ + _(filters.get("budget_against")) + + ":Link/%s:150" % (filters.get("budget_against")), + _("Account") + ":Link/Account:150", + ] - group_months = False if filters["period"] == "Monthly" else True + group_months = False if filters["period"] == "Monthly" else True - fiscal_year = get_fiscal_years(filters) + fiscal_year = get_fiscal_years(filters) - for year in fiscal_year: - for from_date, to_date in get_period_date_ranges(filters["period"], year[0]): - if filters["period"] == "Yearly": - labels = [ - _("Budget") + " " + str(year[0]), - _("Actual ") + " " + str(year[0]), - _("Variance ") + " " + str(year[0]), - ] - for label in labels: - columns.append(label + ":Float:150") - else: - for label in [ - _("Budget") + " (%s)" + " " + str(year[0]), - _("Actual") + " (%s)" + " " + str(year[0]), - _("Variance") + " (%s)" + " " + str(year[0]), - ]: - if group_months: - label = label % ( - formatdate(from_date, format_string="MMM") - + "-" - + formatdate(to_date, format_string="MMM") - ) - else: - label = label % formatdate(from_date, format_string="MMM") + for year in fiscal_year: + for from_date, to_date in get_period_date_ranges(filters["period"], year[0]): + if filters["period"] == "Yearly": + labels = [ + _("Budget") + " " + str(year[0]), + _("Actual ") + " " + str(year[0]), + _("Variance ") + " " + str(year[0]), + ] + for label in labels: + columns.append(label + ":Float:150") + else: + for label in [ + _("Budget") + " (%s)" + " " + str(year[0]), + _("Actual") + " (%s)" + " " + str(year[0]), + _("Variance") + " (%s)" + " " + str(year[0]), + ]: + if group_months: + label = label % ( + formatdate(from_date, format_string="MMM") + + "-" + + formatdate(to_date, format_string="MMM") + ) + else: + label = label % formatdate(from_date, format_string="MMM") - columns.append(label + ":Float:150") + columns.append(label + ":Float:150") - if filters["period"] != "Yearly": - return columns + [ - _("Total Budget") + ":Float:150", - _("Total Actual") + ":Float:150", - _("Total Variance") + ":Float:150", - ] - else: - return columns + if filters["period"] != "Yearly": + return columns + [ + _("Total Budget") + ":Float:150", + _("Total Actual") + ":Float:150", + _("Total Variance") + ":Float:150", + ] + else: + return columns def get_cost_centers(filters): - order_by = "" - if filters.get("budget_against") == "Cost Center": - order_by = "order by lft" + order_by = "" + if filters.get("budget_against") == "Cost Center": + order_by = "order by lft" - if filters.get("budget_against") in ["Cost Center", "Project"]: - return frappe.db.sql_list( - """ + if filters.get("budget_against") in ["Cost Center", "Project"]: + return frappe.db.sql_list( + """ select name from `tab{tab}` where - company=%s + company = %s {order_by} """.format( - tab=filters.get("budget_against"), order_by=order_by - ), - filters.get("company"), - ) - else: - return frappe.db.sql_list( - """ + tab=filters.get("budget_against"), order_by=order_by + ), + filters.get("company"), + ) + else: + return frappe.db.sql_list( + """ select name from `tab{tab}` """.format( - tab=filters.get("budget_against") - ) - ) # nosec + tab=filters.get("budget_against") + ) + ) # nosec # Get dimension & target details def get_dimension_target_details(filters): - budget_against = frappe.scrub(filters.get("budget_against")) + budget_against = frappe.scrub(filters.get("budget_against")) + cond = "" + if filters.get("budget_against_filter"): + cond += """ + and + b.{budget_against} in ( + %s + ) + """.format( + budget_against=budget_against + ) % ", ".join(["%s"] * len(filters.get("budget_against_filter"))) - cond = "" - if filters.get("budget_against_filter"): - cond += """ - and - b.{budget_against} in ( - %s - ) - """.format( - budget_against=budget_against - ) % ", ".join( - ["%s"] * len(filters.get("budget_against_filter")) - ) - - return frappe.db.sql( - """ - select distinct - b.{budget_against} as name + return frappe.db.sql( + """ + select + b.{budget_against} as budget_against, + b.monthly_distribution, + ba.account, + ba.budget_amount, + b.fiscal_year from - `tabBudget` b + `tabBudget` b, + `tabBudget Account` ba where - b.docstatus = 1 + b.name = ba.parent + and b.docstatus = 1 and b.fiscal_year between %s and %s and b.budget_against = %s and b.company = %s @@ -178,65 +179,66 @@ def get_dimension_target_details(filters): order by b.fiscal_year """.format( - budget_against=budget_against, cond=cond - ), - tuple( - [ - filters.from_fiscal_year, - filters.to_fiscal_year, - filters.budget_against, - filters.company, - ] - + filters.get("budget_against_filter") - ), - as_dict=True, - ) + budget_against=budget_against, + cond=cond, + ), + tuple( + [ + filters.from_fiscal_year, + filters.to_fiscal_year, + filters.budget_against, + filters.company, + ] + + filters.get("budget_against_filter") + ), + as_dict=True, + ) # Get target distribution details of accounts of cost center def get_target_distribution_details(filters): - target_details = {} - for d in frappe.db.sql( - """ - select - md.name, - mdp.month, - mdp.percentage_allocation - from - `tabMonthly Distribution Percentage` mdp, - `tabMonthly Distribution` md - where - mdp.parent = md.name - and md.fiscal_year between %s - and %s - order by md.fiscal_year - """, - (filters.from_fiscal_year, filters.to_fiscal_year), - as_dict=1, - ): - target_details.setdefault(d.name, {}).setdefault( - d.month, flt(d.percentage_allocation) - ) + target_details = {} + for d in frappe.db.sql( + """ + select + md.name, + mdp.month, + mdp.percentage_allocation + from + `tabMonthly Distribution Percentage` mdp, + `tabMonthly Distribution` md + where + mdp.parent = md.name + and md.fiscal_year between %s and %s + order by + md.fiscal_year + """, + (filters.from_fiscal_year, filters.to_fiscal_year), + as_dict=1, + ): + target_details.setdefault(d.name, {}).setdefault( + d.month, flt(d.percentage_allocation) + ) - return target_details + return target_details # Get actual details from gl entry def get_actual_details(name, filters): - budget_against = frappe.scrub(filters.get("budget_against")) + budget_against = frappe.scrub(filters.get("budget_against")) - cond = "" - if filters.get("budget_against") == "Cost Center": - cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) - cond = """ - and lft >= \'{lft}\' - and rgt <= \'{rgt}\' + cond = "" + if filters.get("budget_against") == "Cost Center": + cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) + cond = """ + and lft >= "{lft}" + and rgt <= "{rgt}" """.format( - lft=cc_lft, rgt=cc_rgt - ) + lft=cc_lft, rgt=cc_rgt + ) - ac_details = frappe.db.sql( - """ + ac_details = frappe.db.sql( + """ select gl.account, gl.debit, @@ -268,65 +270,56 @@ def get_actual_details(name, filters): gl.name order by gl.fiscal_year """.format( - tab=filters.budget_against, budget_against=budget_against, cond=cond - ), - (filters.from_fiscal_year, filters.to_fiscal_year, name), - as_dict=1, - ) + tab=filters.budget_against, budget_against=budget_against, cond=cond + ), + (filters.from_fiscal_year, filters.to_fiscal_year, name), + as_dict=1, + ) - cc_actual_details = {} - for d in ac_details: - cc_actual_details.setdefault(d.account, []).append(d) + cc_actual_details = {} + for d in ac_details: + cc_actual_details.setdefault(d.account, []).append(d) - return cc_actual_details + return cc_actual_details def get_dimension_account_month_map(filters): - dimension_target_details = get_dimension_target_details(filters) - tdd = get_target_distribution_details(filters) + dimension_target_details = get_dimension_target_details(filters) + tdd = get_target_distribution_details(filters) - cam_map = {} + cam_map = {} - for ccd in dimension_target_details: - accounts = get_accounts(ccd.name, filters) - actual_details = get_actual_details(ccd.name, filters) + for ccd in dimension_target_details: + actual_details = get_actual_details(ccd.budget_against, filters) - for year in get_fiscal_years(filters): - year = year[0] - monthly_distribution = get_monthly_distribution(ccd.name, year, filters) - for month_id in range(1, 13): - month = datetime.date(2013, month_id, 1).strftime( - "%B" - ) # Get month string + for month_id in range(1, 13): + month = datetime.date(2013, month_id, 1).strftime("%B") + cam_map.setdefault(ccd.budget_against, {}).setdefault( + ccd.account, {} + ).setdefault(ccd.fiscal_year, {}).setdefault( + month, frappe._dict({"target": 0.0, "actual": 0.0}) + ) - for account in accounts: - account = account[0] - cam_map.setdefault(ccd.name, {}).setdefault(account, {}).setdefault( - year, {} - ).setdefault(month, frappe._dict({"target": 0.0, "actual": 0.0})) + tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month] + month_percentage = ( + tdd.get(ccd.monthly_distribution, {}).get(month, 0) + if ccd.monthly_distribution + else 100.0 / 12 + ) - tav_dict = cam_map[ccd.name][account][year][month] + tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100 - month_percentage = ( - tdd.get(monthly_distribution, {}).get(month, 0) - if monthly_distribution - else 100.0 / 12 - ) + for ad in actual_details.get(ccd.account, []): + if ad.month_name == month and ad.fiscal_year == ccd.fiscal_year: + tav_dict.actual += flt(ad.debit) - flt(ad.credit) - budget_amount = get_budget_amount(ccd.name, year, account, filters) - - tav_dict.target = flt(budget_amount) * month_percentage / 100 - - for ad in actual_details.get(account, []): - if ad.month_name == month and ad.fiscal_year == year: - tav_dict.actual += flt(ad.debit) - flt(ad.credit) - return cam_map + return cam_map def get_fiscal_years(filters): - fiscal_year = frappe.db.sql( - """ + fiscal_year = frappe.db.sql( + """ select name from @@ -336,85 +329,10 @@ def get_fiscal_years(filters): order by year """, - { - "from_fiscal_year": filters["from_fiscal_year"], - "to_fiscal_year": filters["to_fiscal_year"], - }, - ) + { + "from_fiscal_year": filters["from_fiscal_year"], + "to_fiscal_year": filters["to_fiscal_year"], + }, + ) - return fiscal_year - - -def get_accounts(name, filters): - budget_against = frappe.scrub(filters.get("budget_against")) - - accounts = frappe.db.sql( - """ - select - distinct(ba.account) - from - `tabBudget Account` ba - join - `tabBudget` b - on b.name = ba.parent - where - b.docstatus = 1 - and b.fiscal_year between %s and %s - and b.{budget_against} = %s - order by - ba.account - """.format( - budget_against=budget_against - ), - (filters.from_fiscal_year, filters.to_fiscal_year, name), - ) - - return accounts - - -def get_monthly_distribution(name, year, filters): - budget_against = frappe.scrub(filters.get("budget_against")) - - monthly_distribution = frappe.db.sql( - """ - select - monthly_distribution - from - `tabBudget` - where - docstatus = 1 - and {budget_against} = %s - and fiscal_year = %s - """.format( - budget_against=budget_against - ), - (name, year), - ) - - return monthly_distribution[0][0] if monthly_distribution else None - - -def get_budget_amount(name, year, account, filters): - budget_against = frappe.scrub(filters.get("budget_against")) - - budget_amount = frappe.db.sql( - """ - select - ba.budget_amount - from - `tabBudget Account` ba - join - `tabBudget` b - on b.name = ba.parent - where - b.docstatus = 1 - and b.{budget_against} = %s - and b.fiscal_year = %s - and ba.account = %s - """.format( - budget_against=budget_against - ), - (name, year, account), - ) - - return budget_amount[0][0] if budget_amount else 0 + return fiscal_year From 0f455060abf219b85dca62fea1379f69aeedb0a8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 10 May 2020 17:24:11 +0530 Subject: [PATCH 468/929] fix: Formatting fixes --- .../budget_variance_report.py | 69 +++++-------------- 1 file changed, 19 insertions(+), 50 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 4d5892a9132..49c1d0f2ccd 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -22,9 +22,7 @@ def execute(filters=None): else: dimensions = get_cost_centers(filters) - period_month_ranges = get_period_month_ranges( - filters["period"], filters["from_fiscal_year"] - ) + period_month_ranges = get_period_month_ranges(filters["period"], filters["from_fiscal_year"]) cam_map = get_dimension_account_month_map(filters) data = [] @@ -41,9 +39,7 @@ def execute(filters=None): for month in relevant_months: if monthwise_data.get(year[0]): month_data = monthwise_data.get(year[0]).get(month, {}) - for i, fieldname in enumerate( - ["target", "actual", "variance"] - ): + for i, fieldname in enumerate(["target", "actual", "variance"]): value = flt(month_data.get(fieldname)) period_data[i] += value totals[i] += value @@ -67,7 +63,7 @@ def get_columns(filters): columns = [ _(filters.get("budget_against")) + ":Link/%s:150" % (filters.get("budget_against")), - _("Account") + ":Link/Account:150", + _("Account") + ":Link/Account:150" ] group_months = False if filters["period"] == "Monthly" else True @@ -80,7 +76,7 @@ def get_columns(filters): labels = [ _("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), - _("Variance ") + " " + str(year[0]), + _("Variance ") + " " + str(year[0]) ] for label in labels: columns.append(label + ":Float:150") @@ -88,7 +84,7 @@ def get_columns(filters): for label in [ _("Budget") + " (%s)" + " " + str(year[0]), _("Actual") + " (%s)" + " " + str(year[0]), - _("Variance") + " (%s)" + " " + str(year[0]), + _("Variance") + " (%s)" + " " + str(year[0]) ]: if group_months: label = label % ( @@ -105,7 +101,7 @@ def get_columns(filters): return columns + [ _("Total Budget") + ":Float:150", _("Total Actual") + ":Float:150", - _("Total Variance") + ":Float:150", + _("Total Variance") + ":Float:150" ] else: return columns @@ -126,11 +122,8 @@ def get_cost_centers(filters): where company = %s {order_by} - """.format( - tab=filters.get("budget_against"), order_by=order_by - ), - filters.get("company"), - ) + """.format(tab=filters.get("budget_against"), order_by=order_by), + filters.get("company")) else: return frappe.db.sql_list( """ @@ -138,10 +131,7 @@ def get_cost_centers(filters): name from `tab{tab}` - """.format( - tab=filters.get("budget_against") - ) - ) # nosec + """.format(tab=filters.get("budget_against"))) # nosec # Get dimension & target details @@ -149,14 +139,8 @@ def get_dimension_target_details(filters): budget_against = frappe.scrub(filters.get("budget_against")) cond = "" if filters.get("budget_against_filter"): - cond += """ - and - b.{budget_against} in ( - %s - ) - """.format( - budget_against=budget_against - ) % ", ".join(["%s"] * len(filters.get("budget_against_filter"))) + cond += """ and b.{budget_against} in (%s)""".format( + budget_against=budget_against) % ", ".join(["%s"] * len(filters.get("budget_against_filter"))) return frappe.db.sql( """ @@ -190,9 +174,7 @@ def get_dimension_target_details(filters): filters.company, ] + filters.get("budget_against_filter") - ), - as_dict=True, - ) + ), as_dict=True) # Get target distribution details of accounts of cost center @@ -213,29 +195,24 @@ def get_target_distribution_details(filters): order by md.fiscal_year """, - (filters.from_fiscal_year, filters.to_fiscal_year), - as_dict=1, - ): + (filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1): target_details.setdefault(d.name, {}).setdefault( d.month, flt(d.percentage_allocation) ) return target_details - # Get actual details from gl entry def get_actual_details(name, filters): budget_against = frappe.scrub(filters.get("budget_against")) - cond = "" + if filters.get("budget_against") == "Cost Center": cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) cond = """ and lft >= "{lft}" and rgt <= "{rgt}" - """.format( - lft=cc_lft, rgt=cc_rgt - ) + """.format(lft=cc_lft, rgt=cc_rgt) ac_details = frappe.db.sql( """ @@ -269,12 +246,8 @@ def get_actual_details(name, filters): group by gl.name order by gl.fiscal_year - """.format( - tab=filters.budget_against, budget_against=budget_against, cond=cond - ), - (filters.from_fiscal_year, filters.to_fiscal_year, name), - as_dict=1, - ) + """.format(tab=filters.budget_against, budget_against=budget_against, cond=cond), + (filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1) cc_actual_details = {} for d in ac_details: @@ -282,7 +255,6 @@ def get_actual_details(name, filters): return cc_actual_details - def get_dimension_account_month_map(filters): dimension_target_details = get_dimension_target_details(filters) tdd = get_target_distribution_details(filters) @@ -326,13 +298,10 @@ def get_fiscal_years(filters): `tabFiscal Year` where name between %(from_fiscal_year)s and %(to_fiscal_year)s - order by - year """, { "from_fiscal_year": filters["from_fiscal_year"], - "to_fiscal_year": filters["to_fiscal_year"], - }, - ) + "to_fiscal_year": filters["to_fiscal_year"] + }) return fiscal_year From 00bbc7660531e6338f0334939177db05648d5d5d Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Mon, 11 May 2020 19:28:49 +0530 Subject: [PATCH 469/929] fix(patch): use translated string while setting notification template (#21678) --- erpnext/patches/v11_0/set_default_email_template_in_hr.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v11_0/set_default_email_template_in_hr.py b/erpnext/patches/v11_0/set_default_email_template_in_hr.py index 14954fbeb31..46223761095 100644 --- a/erpnext/patches/v11_0/set_default_email_template_in_hr.py +++ b/erpnext/patches/v11_0/set_default_email_template_in_hr.py @@ -1,8 +1,9 @@ from __future__ import unicode_literals +from frappe import _ import frappe def execute(): hr_settings = frappe.get_single("HR Settings") - hr_settings.leave_approval_notification_template = "Leave Approval Notification" - hr_settings.leave_status_notification_template = "Leave Status Notification" - hr_settings.save() \ No newline at end of file + hr_settings.leave_approval_notification_template = _("Leave Approval Notification") + hr_settings.leave_status_notification_template = _("Leave Status Notification") + hr_settings.save() From c8ed6b1e79a929708a6c6aea92975e78e2baabd1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 May 2020 19:29:18 +0530 Subject: [PATCH 470/929] fix: Run income-tax-slab patch only if slab already exists in payroll period (#21684) (#21687) (cherry picked from commit 85a89812a42a8d8e5f80dbec89c77e5fe5e82988) Co-authored-by: Nabin Hait --- ...ve_tax_slabs_from_payroll_period_to_income_tax_slab.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py index 179be2cfde5..ec94cd01d1e 100644 --- a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py +++ b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py @@ -7,7 +7,7 @@ import frappe from frappe.model.utils.rename_field import rename_field def execute(): - if not frappe.db.table_exists("Payroll Period"): + if not (frappe.db.table_exists("Payroll Period") and frappe.db.table_exists("Taxable Salary Slab")): return for doctype in ("income_tax_slab", "salary_structure_assignment", "employee_other_income", "income_tax_slab_other_charges"): @@ -60,6 +60,9 @@ def execute(): """, (income_tax_slab.name, company.name, period.start_date)) # move other incomes to separate document + if not frappe.db.table_exists("Employee Tax Exemption Proof Submission"): + return + migrated = [] proofs = frappe.get_all("Employee Tax Exemption Proof Submission", filters = {'docstatus': 1}, @@ -79,6 +82,9 @@ def execute(): except: pass + if not frappe.db.table_exists("Employee Tax Exemption Declaration"): + return + declerations = frappe.get_all("Employee Tax Exemption Declaration", filters = {'docstatus': 1}, fields =['payroll_period', 'employee', 'company', 'income_from_other_sources'] From 855373a2539d4084bbf55f312091bcfe3d6400c7 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 May 2020 21:26:18 +0530 Subject: [PATCH 471/929] fix: Message for missing valuation rate (#21686) (#21688) (cherry picked from commit 97715f2877320486353a17662ea5f57f8eaf4f8c) Co-authored-by: Marica --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- erpnext/stock/stock_ledger.py | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index f3737ac5f7d..eb3e3e9af4f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -467,7 +467,7 @@ class StockEntry(StockController): "qty": item.s_warehouse and -1*flt(item.transfer_qty) or flt(item.transfer_qty), "serial_no": item.serial_no, "voucher_type": self.doctype, - "voucher_no": item.name, + "voucher_no": self.name, "company": self.company, "allow_zero_valuation": item.allow_zero_valuation_rate, }) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 7567a1ae758..ba885c09487 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -527,7 +527,16 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, if not allow_zero_rate and not valuation_rate and raise_error_if_no_rate \ and cint(erpnext.is_perpetual_inventory_enabled(company)): frappe.local.message_log = [] - frappe.throw(_("Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a zero valuation rate item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting / cancelling this entry.") - .format(item_code, voucher_type, voucher_no)) + form_link = frappe.utils.get_link_to_form("Item", item_code) + + message = _("Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}.").format(form_link, voucher_type, voucher_no) + message += "

" + _(" Here are the options to proceed:") + solutions = "
  • " + _("If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table.").format(voucher_type) + "
  • " + solutions += "
  • " + _("If not, you can Cancel / Submit this entry ") + _("{0}").format(frappe.bold("after")) + _(" performing either one below:") + "
  • " + sub_solutions = "
    • " + _("Create an incoming stock transaction for the Item.") + "
    • " + sub_solutions += "
    • " + _("Mention Valuation Rate in the Item master.") + "
    " + msg = message + solutions + sub_solutions + "" + + frappe.throw(msg=msg, title=_("Valuation Rate Missing")) return valuation_rate From b47f5830f42446f3f910b1ee43680a24e8b3a054 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 12 May 2020 14:11:33 +0530 Subject: [PATCH 472/929] fix: purchase inv shows overdue for fraction of outstanding --- .../purchase_invoice/purchase_invoice.py | 34 +++++++++++++++++++ erpnext/controllers/status_updater.py | 11 ------ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 3654f6ef032..2feeea0bd6e 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -981,6 +981,40 @@ class PurchaseInvoice(BuyingController): # calculate totals again after applying TDS self.calculate_taxes_and_totals() + + def set_status(self, update=False, status=None, update_modified=True): + if self.is_new(): + if self.get('amended_from'): + self.status = 'Draft' + return + + precision = self.precision("outstanding_amount") + outstanding_amount = flt(self.outstanding_amount, precision) + due_date = getdate(self.due_date) + nowdate = getdate() + + if not status: + if self.docstatus == 2: + status = "Cancelled" + elif self.docstatus == 1: + elif outstanding_amount > 0 and due_date < nowdate: + self.status = "Overdue" + elif outstanding_amount > 0 and due_date >= nowdate: + self.status = "Unpaid" + #Check if outstanding amount is 0 due to debit note issued against invoice + elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): + self.status = "Debit Note Issued" + elif self.is_return == 1: + self.status = "Return" + elif outstanding_amount<=0: + self.status = "Paid" + else: + self.status = "Submitted" + else: + self.status = "Draft" + + if update: + self.db_set('status', self.status, update_modified = update_modified) def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index de76e45cd11..b465a106f0e 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -69,17 +69,6 @@ status_map = { ["Cancelled", "eval:self.docstatus==2"], ["Closed", "eval:self.status=='Closed'"], ], - "Purchase Invoice": [ - ["Draft", None], - ["Submitted", "eval:self.docstatus==1"], - ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"], - ["Return", "eval:self.is_return==1 and self.docstatus==1"], - ["Debit Note Issued", - "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], - ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], - ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], - ["Cancelled", "eval:self.docstatus==2"], - ], "Material Request": [ ["Draft", None], ["Stopped", "eval:self.status == 'Stopped'"], From cb1cc96210c08e0e278a00781ad1afd2bc561d17 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 13 May 2020 16:30:10 +0530 Subject: [PATCH 473/929] fix: item tax template fetching in offline pos --- erpnext/accounts/doctype/sales_invoice/pos.py | 3 +- erpnext/accounts/page/pos/pos.js | 31 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 16e918cc728..c2f79841725 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -180,10 +180,11 @@ def get_items_list(pos_profile, company): i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no, i.has_serial_no, i.is_stock_item, i.brand, i.stock_uom, i.image, id.expense_account, id.selling_cost_center, id.default_warehouse, - i.sales_uom, c.conversion_factor + i.sales_uom, c.conversion_factor, it.item_tax_template, it.valid_from from `tabItem` i left join `tabItem Default` id on id.parent = i.name and id.company = %s + left join `tabItem Tax` it on it.parent = i.name left join `tabUOM Conversion Detail` c on i.name = c.parent and i.sales_uom = c.uom where i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1 diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 24fcb41a5db..06575ac3a69 100755 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -1458,7 +1458,36 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.child.batch_no = this.item_batch_no[this.child.item_code]; this.child.serial_no = (this.item_serial_no[this.child.item_code] ? this.item_serial_no[this.child.item_code][0] : ''); - this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_code]); + + const tax_template_is_valid = frappe.datetime.get_diff(frappe.datetime.now_date(), this.items[0].valid_from) > 0; + + this.child.item_tax_template = tax_template_is_valid ? this.items[0].item_tax_template : ''; + this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_tax_template]); + + if (this.child.item_tax_rate) { + this.add_taxes_from_item_tax_template(this.child.item_tax_rate); + } + }, + + add_taxes_from_item_tax_template: function(item_tax_map) { + let me = this; + + if(item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) { + if(typeof (item_tax_map) == "string") { + item_tax_map = JSON.parse(item_tax_map); + } + + $.each(item_tax_map, function(tax, rate) { + let found = (me.frm.doc.taxes || []).find(d => d.account_head === tax); + if(!found) { + let child = frappe.model.add_child(me.frm.doc, "taxes"); + child.charge_type = "On Net Total"; + child.account_head = tax; + child.description = String(tax); + child.rate = rate; + } + }); + } }, update_paid_amount_status: function (update_paid_amount) { From 6a317a8e78efd40f7ee8d3a66a94156a3725f4da Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 13 May 2020 18:58:03 +0530 Subject: [PATCH 474/929] fix: Add total rows in Report Purchase Order Items to be Received or Billed (#21711) --- .../purchase_order_items_to_be_received_or_billed.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/purchase_order_items_to_be_received_or_billed.json b/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/purchase_order_items_to_be_received_or_billed.json index 48c0f423fd9..6714b2e02c9 100644 --- a/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/purchase_order_items_to_be_received_or_billed.json +++ b/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/purchase_order_items_to_be_received_or_billed.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "creation": "2019-09-16 14:10:33.102865", "disable_prepared_report": 0, "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2019-09-21 15:19:55.710578", + "modified": "2020-05-13 15:27:45.228418", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Order Items To Be Received or Billed", From 12739ec464b0079ccf5b185c3b5fc6b42cb0a99d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 13 May 2020 22:02:56 +0530 Subject: [PATCH 475/929] fix: invalid conditional statement --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 2feeea0bd6e..978898a54b2 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -997,7 +997,7 @@ class PurchaseInvoice(BuyingController): if self.docstatus == 2: status = "Cancelled" elif self.docstatus == 1: - elif outstanding_amount > 0 and due_date < nowdate: + if outstanding_amount > 0 and due_date < nowdate: self.status = "Overdue" elif outstanding_amount > 0 and due_date >= nowdate: self.status = "Unpaid" From b432f3358e54d46ff236c46091009c6c3bd3bc4a Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 15 May 2020 04:25:10 +0530 Subject: [PATCH 476/929] fix: item price not fetching when customer is unset in item price (#21489) * fix: item price not fetching when customer is unset in item price * fix: item price of selling type has hidden supplier value * fix: remove test variable * fix: test * Update erpnext/stock/get_item_details.py Co-authored-by: Marica * patch: invalid customer/supplier based on item price type * fix: remove patches from develop branch * fix: patch Co-authored-by: Marica --- erpnext/patches.txt | 3 ++- ...stomer_supplier_based_on_type_of_item_price.py | 15 +++++++++++++++ erpnext/stock/doctype/item_price/item_price.py | 7 +++++++ erpnext/stock/get_item_details.py | 2 +- 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 69291af9cd2..5666700a935 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -662,4 +662,5 @@ erpnext.patches.v12_0.set_updated_purpose_in_pick_list erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 -execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") \ No newline at end of file +execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") +erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price diff --git a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py new file mode 100644 index 00000000000..60aec05466a --- /dev/null +++ b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py @@ -0,0 +1,15 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + invalid_selling_item_price = frappe.db.sql( + """SELECT name FROM `tabItem Price` WHERE selling = 1 and buying = 0 and (supplier IS NOT NULL or supplier = '')""" + ) + invalid_buying_item_price = frappe.db.sql( + """SELECT name FROM `tabItem Price` WHERE selling = 0 and buying = 1 and (customer IS NOT NULL or customer = '')""" + ) + docs_to_modify = invalid_buying_item_price + invalid_selling_item_price + for d in docs_to_modify: + # saving the doc will auto reset invalid customer/supplier field + doc = frappe.get_doc("Item Price", d[0]) + doc.save() \ No newline at end of file diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index 957c41546b3..8e39eb5037d 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -69,3 +69,10 @@ class ItemPrice(Document): self.reference = self.customer if self.buying: self.reference = self.supplier + + if self.selling and not self.buying: + # if only selling then remove supplier + self.supplier = None + if self.buying and not self.selling: + # if only buying then remove customer + self.customer = None diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index f43592bef63..50c8c4e53f4 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -624,7 +624,7 @@ def get_item_price(args, item_code, ignore_party=False): elif args.get("supplier"): conditions += " and supplier=%(supplier)s" else: - conditions += " and (customer is null or customer = '') and (supplier is null or supplier = '')" + conditions += "and (customer is null or customer = '') and (supplier is null or supplier = '')" if args.get('transaction_date'): conditions += """ and %(transaction_date)s between From 0af125f6fe721716773f8b6484f60f8f949d86ce Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 15 May 2020 05:26:41 +0530 Subject: [PATCH 477/929] fix: test --- erpnext/accounts/doctype/pos_profile/test_pos_profile.py | 6 +++--- erpnext/accounts/doctype/sales_invoice/pos.py | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py index 58f12162d14..f8d52a78336 100644 --- a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py +++ b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py @@ -15,12 +15,12 @@ class TestPOSProfile(unittest.TestCase): pos_profile = get_pos_profile("_Test Company") or {} if pos_profile: doc = frappe.get_doc("POS Profile", pos_profile.get("name")) - doc.append('item_groups', {'item_group': '_Test Item Group'}) - doc.append('customer_groups', {'customer_group': '_Test Customer Group'}) + doc.set('item_groups', [{'item_group': '_Test Item Group'}]) + doc.set('customer_groups', [{'customer_group': '_Test Customer Group'}]) doc.save() items = get_items_list(doc, doc.company) customers = get_customers_list(doc) - + products_count = frappe.db.sql(""" select count(name) from tabItem where item_group = '_Test Item Group'""", as_list=1) customers_count = frappe.db.sql(""" select count(name) from tabCustomer where customer_group = '_Test Customer Group'""") diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index c2f79841725..f371bd425c5 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -189,6 +189,7 @@ def get_items_list(pos_profile, company): where i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1 {cond} + group by i.item_code """.format(cond=cond), tuple([company] + args_list), as_dict=1) From 71d9a52a079fc7c0018a14ca61c27422f013f0b4 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 15 May 2020 11:55:36 +0530 Subject: [PATCH 478/929] fix: duplicate leave expiry creation (#21506) * fix: validate existing ledger entries to avoid duplicates * patch: remove duplicate ledger entries created * fix: consider only submitted ledger entries * fix: delete duplicate leaves from the ledger * fix: check if duplicate ledger entry exists * chore: formatting changes Co-authored-by: Nabin Hait --- .../leave_application/leave_application.py | 6 +-- .../leave_ledger_entry/leave_ledger_entry.py | 50 +++++++++++-------- erpnext/patches.txt | 1 + .../remove_duplicate_leave_ledger_entries.py | 44 ++++++++++++++++ 4 files changed, 78 insertions(+), 23 deletions(-) create mode 100644 erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index c870cc5d5d0..6ef4a303d87 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -541,7 +541,7 @@ def get_remaining_leaves(allocation, leaves_taken, date, expiry): return _get_remaining_leaves(total_leaves, allocation.to_date) -def get_leaves_for_period(employee, leave_type, from_date, to_date): +def get_leaves_for_period(employee, leave_type, from_date, to_date, do_not_skip_expired_leaves=False): leave_entries = get_leave_entries(employee, leave_type, from_date, to_date) leave_days = 0 @@ -551,8 +551,8 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date): if inclusive_period and leave_entry.transaction_type == 'Leave Encashment': leave_days += leave_entry.leaves - elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' \ - and leave_entry.is_expired and not skip_expiry_leaves(leave_entry, to_date): + elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' and leave_entry.is_expired \ + and (do_not_skip_expired_leaves or not skip_expiry_leaves(leave_entry, to_date)): leave_days += leave_entry.leaves elif leave_entry.transaction_type == 'Leave Application': diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 9ed58c9e59b..63559c4f5ae 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -88,32 +88,40 @@ def get_previous_expiry_ledger_entry(ledger): }, fieldname=['name']) def process_expired_allocation(): - ''' Check if a carry forwarded allocation has expired and create a expiry ledger entry ''' + ''' Check if a carry forwarded allocation has expired and create a expiry ledger entry + Case 1: carry forwarded expiry period is set for the leave type, + create a separate leave expiry entry against each entry of carry forwarded and non carry forwarded leaves + Case 2: leave type has no specific expiry period for carry forwarded leaves + and there is no carry forwarded leave allocation, create a single expiry against the remaining leaves. + ''' # fetch leave type records that has carry forwarded leaves expiry leave_type_records = frappe.db.get_values("Leave Type", filters={ 'expire_carry_forwarded_leaves_after_days': (">", 0) }, fieldname=['name']) - leave_type = [record[0] for record in leave_type_records] + leave_type = [record[0] for record in leave_type_records] or [''] - expired_allocation = frappe.db.sql_list("""SELECT name - FROM `tabLeave Ledger Entry` - WHERE - `transaction_type`='Leave Allocation' - AND `is_expired`=1""") - - expire_allocation = frappe.get_all("Leave Ledger Entry", - fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], - filters={ - 'to_date': ("<", today()), - 'transaction_type': 'Leave Allocation', - 'transaction_name': ('not in', expired_allocation) - }, - or_filters={ - 'is_carry_forward': 0, - 'leave_type': ('in', leave_type) - }) + # fetch non expired leave ledger entry of transaction_type allocation + expire_allocation = frappe.db.sql(""" + SELECT + leaves, to_date, employee, leave_type, + is_carry_forward, transaction_name as name, transaction_type + FROM `tabLeave Ledger Entry` l + WHERE (NOT EXISTS + (SELECT name + FROM `tabLeave Ledger Entry` + WHERE + transaction_name = l.transaction_name + AND transaction_type = 'Leave Allocation' + AND name<>l.name + AND docstatus = 1 + AND ( + is_carry_forward=l.is_carry_forward + OR (is_carry_forward = 0 AND leave_type not in %s) + ))) + AND transaction_type = 'Leave Allocation' + AND to_date < %s""", (leave_type, today()), as_dict=1) if expire_allocation: create_expiry_ledger_entry(expire_allocation) @@ -133,6 +141,7 @@ def get_remaining_leaves(allocation): 'employee': allocation.employee, 'leave_type': allocation.leave_type, 'to_date': ('<=', allocation.to_date), + 'docstatus': 1 }, fieldname=['SUM(leaves)']) @frappe.whitelist() @@ -159,7 +168,8 @@ def expire_allocation(allocation, expiry_date=None): def expire_carried_forward_allocation(allocation): ''' Expires remaining leaves in the on carried forward allocation ''' from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period - leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type, allocation.from_date, allocation.to_date) + leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type, + allocation.from_date, allocation.to_date, do_not_skip_expired_leaves=True) leaves = flt(allocation.leaves) + flt(leaves_taken) # allow expired leaves entry to be created diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 5666700a935..30ec5be4902 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -662,5 +662,6 @@ erpnext.patches.v12_0.set_updated_purpose_in_pick_list erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 +erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py new file mode 100644 index 00000000000..98a2fcf27ea --- /dev/null +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -0,0 +1,44 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + """Delete duplicate leave ledger entries of type allocation created.""" + if not frappe.db.a_row_exists("Leave Ledger Entry"): + return + + duplicate_records_list = get_duplicate_records() + delete_duplicate_ledger_entries(duplicate_records_list) + +def get_duplicate_records(): + """Fetch all but one duplicate records from the list of expired leave allocation.""" + return frappe.db.sql_list(""" + WITH duplicate_records AS + (SELECT + name, transaction_name, is_carry_forward, + ROW_NUMBER() over(partition by transaction_name order by creation)as row + FROM `tabLeave Ledger Entry` l + WHERE (EXISTS + (SELECT name + FROM `tabLeave Ledger Entry` + WHERE + transaction_name = l.transaction_name + AND transaction_type = 'Leave Allocation' + AND name <> l.name + AND employee = l.employee + AND docstatus = 1 + AND leave_type = l.leave_type + AND is_carry_forward=l.is_carry_forward + AND to_date = l.to_date + AND from_date = l.from_date + AND is_expired = 1 + ))) + SELECT name FROM duplicate_records WHERE row > 1 + """) + +def delete_duplicate_ledger_entries(duplicate_records_list): + """Delete duplicate leave ledger entries.""" + if duplicate_records_list: + frappe.db.sql(''' DELETE FROM `tabLeave Ledger Entry` WHERE name in {0}'''.format(tuple(duplicate_records_list))) #nosec \ No newline at end of file From f34436906880e35929aec8600bd38a61b4638028 Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 15 May 2020 12:09:16 +0530 Subject: [PATCH 479/929] format: better error messages for invalid coupon codes (v12) (#21598) * format: better error messages for invalid coupon codes * fix: remove unnecessary docstatus check --- .../accounts/doctype/pricing_rule/utils.py | 30 +++++++------ erpnext/shopping_cart/cart.py | 44 ++++++++++--------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index b358f56671f..cb05481df5a 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -4,13 +4,19 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe, copy, json -from frappe import throw, _ + +import copy +import json + from six import string_types -from frappe.utils import flt, cint, get_datetime, get_link_to_form, today + +import frappe from erpnext.setup.doctype.item_group.item_group import get_child_item_groups from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses from erpnext.stock.get_item_details import get_conversion_factor +from frappe import _, throw +from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today + class MultiplePricingRuleConflict(frappe.ValidationError): pass @@ -502,18 +508,16 @@ def get_pricing_rule_items(pr_doc): return list(set(apply_on_data)) def validate_coupon_code(coupon_name): - from frappe.utils import today,getdate - coupon=frappe.get_doc("Coupon Code",coupon_name) + coupon = frappe.get_doc("Coupon Code", coupon_name) + if coupon.valid_from: - if coupon.valid_from > getdate(today()) : - frappe.throw(_("Sorry,coupon code validity has not started")) + if coupon.valid_from > getdate(today()): + frappe.throw(_("Sorry, this coupon code's validity has not started")) elif coupon.valid_upto: - if coupon.valid_upto < getdate(today()) : - frappe.throw(_("Sorry,coupon code validity has expired")) - elif coupon.used>=coupon.maximum_use: - frappe.throw(_("Sorry,coupon code are exhausted")) - else: - return + if coupon.valid_upto < getdate(today()): + frappe.throw(_("Sorry, this coupon code's validity has expired")) + elif coupon.used >= coupon.maximum_use: + frappe.throw(_("Sorry, this coupon code is no longer valid")) def update_coupon_code_count(coupon_name,transaction_type): coupon=frappe.get_doc("Coupon Code",coupon_name) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 4c3ac100d5b..8e644f4144d 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -541,27 +541,31 @@ def show_terms(doc): return doc.tc_name @frappe.whitelist(allow_guest=True) -def apply_coupon_code(applied_code,applied_referral_sales_partner): +def apply_coupon_code(applied_code, applied_referral_sales_partner): quotation = True - if applied_code: - coupon_list=frappe.get_all('Coupon Code', filters={"docstatus": ("<", "2"), 'coupon_code':applied_code }, fields=['name']) - if coupon_list: - coupon_name=coupon_list[0].name - from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code - validate_coupon_code(coupon_name) - quotation = _get_cart_quotation() - quotation.coupon_code=coupon_name + + if not applied_code: + frappe.throw(_("Please enter a coupon code")) + + coupon_list = frappe.get_all('Coupon Code', filters={'coupon_code': applied_code}) + if not coupon_list: + frappe.throw(_("Please enter a valid coupon code")) + + coupon_name = coupon_list[0].name + + from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code + validate_coupon_code(coupon_name) + quotation = _get_cart_quotation() + quotation.coupon_code = coupon_name + quotation.flags.ignore_permissions = True + quotation.save() + + if applied_referral_sales_partner: + sales_partner_list = frappe.get_all('Sales Partner', filters={'referral_code': applied_referral_sales_partner}) + if sales_partner_list: + sales_partner_name = sales_partner_list[0].name + quotation.referral_sales_partner = sales_partner_name quotation.flags.ignore_permissions = True quotation.save() - if applied_referral_sales_partner: - sales_partner_list=frappe.get_all('Sales Partner', filters={'docstatus': 0, 'referral_code':applied_referral_sales_partner }, fields=['name']) - if sales_partner_list: - sales_partner_name=sales_partner_list[0].name - quotation.referral_sales_partner=sales_partner_name - quotation.flags.ignore_permissions = True - quotation.save() - else: - frappe.throw(_("Please enter valid coupon code !!")) - else: - frappe.throw(_("Please enter coupon code !!")) + return quotation From 8ed066ba1f01aa209e7bf793ae3fc086569debf4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 15 May 2020 12:58:19 +0530 Subject: [PATCH 480/929] fix: Add misssing dimensions in GL entries (#21691) * fix: Add misssing dimensions in GL entries * fix: expnese_taxes_and_charges.json * fix: Add project filter in trial balance report * fix: Use current dimensions instead of dimensions from asset --- erpnext/accounts/deferred_revenue.py | 12 +++--- .../accounting_dimension.py | 6 +-- .../invoice_discounting.py | 14 +++++-- .../doctype/payment_entry/payment_entry.py | 8 ++-- .../purchase_invoice/purchase_invoice.py | 13 +++---- .../doctype/sales_invoice/sales_invoice.py | 25 ++++++------- .../report/trial_balance/trial_balance.js | 12 ++++-- .../report/trial_balance/trial_balance.py | 8 ++++ erpnext/assets/doctype/asset/asset.py | 6 +-- erpnext/education/doctype/fees/fees.py | 6 ++- .../hr/doctype/expense_claim/expense_claim.py | 15 ++++---- .../expense_claim_detail.json | 37 +++++++++++++++++-- .../expense_taxes_and_charges.json | 18 +++++++-- erpnext/patches.txt | 2 +- ...counting_dimensions_in_missing_doctypes.py | 3 +- 15 files changed, 125 insertions(+), 60 deletions(-) diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index 62a8f05c659..cbb151f95a3 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -159,7 +159,7 @@ def book_deferred_income_or_expense(doc, posting_date=None): total_days, total_booking_days, account_currency) make_gl_entries(doc, credit_account, debit_account, against, - amount, base_amount, end_date, project, account_currency, item.cost_center, item.name) + amount, base_amount, end_date, project, account_currency, item.cost_center, item) if getdate(end_date) < getdate(posting_date) and not last_gl_entry: _book_deferred_revenue_or_expense(item) @@ -170,7 +170,7 @@ def book_deferred_income_or_expense(doc, posting_date=None): _book_deferred_revenue_or_expense(item) def make_gl_entries(doc, credit_account, debit_account, against, - amount, base_amount, posting_date, project, account_currency, cost_center, voucher_detail_no): + amount, base_amount, posting_date, project, account_currency, cost_center, item): # GL Entry for crediting the amount in the deferred expense from erpnext.accounts.general_ledger import make_gl_entries @@ -184,10 +184,10 @@ def make_gl_entries(doc, credit_account, debit_account, against, "credit": base_amount, "credit_in_account_currency": amount, "cost_center": cost_center, - "voucher_detail_no": voucher_detail_no, + "voucher_detail_no": item.name, 'posting_date': posting_date, 'project': project - }, account_currency) + }, account_currency, item=item) ) # GL Entry to debit the amount from the expense gl_entries.append( @@ -197,10 +197,10 @@ def make_gl_entries(doc, credit_account, debit_account, against, "debit": base_amount, "debit_in_account_currency": amount, "cost_center": cost_center, - "voucher_detail_no": voucher_detail_no, + "voucher_detail_no": item.name, 'posting_date': posting_date, 'project': project - }, account_currency) + }, account_currency, item=item) ) if gl_entries: diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 7a85bfb26b6..894ec5bdec5 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -162,9 +162,9 @@ def toggle_disabling(doc): def get_doctypes_with_dimensions(): doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset", - "Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Sales Invoice Item", "Purchase Invoice Item", - "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", "Purchase Receipt Item", - "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule", + "Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", + "Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", + "Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule", "Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation", "Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription", "Subscription Plan"] diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py index 39fc203d53f..594b4d4a223 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py @@ -8,6 +8,7 @@ from frappe import _ from frappe.utils import flt, getdate, nowdate, add_days from erpnext.controllers.accounts_controller import AccountsController from erpnext.accounts.general_ledger import make_gl_entries +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions class InvoiceDiscounting(AccountsController): def validate(self): @@ -81,10 +82,15 @@ class InvoiceDiscounting(AccountsController): def make_gl_entries(self): company_currency = frappe.get_cached_value('Company', self.company, "default_currency") + gl_entries = [] + invoice_fields = ["debit_to", "party_account_currency", "conversion_rate", "cost_center"] + accounting_dimensions = get_accounting_dimensions() + + invoice_fields.extend(accounting_dimensions) + for d in self.invoices: - inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, - ["debit_to", "party_account_currency", "conversion_rate", "cost_center"], as_dict=1) + inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, invoice_fields, as_dict=1) if d.outstanding_amount: outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate, @@ -102,7 +108,7 @@ class InvoiceDiscounting(AccountsController): "cost_center": inv.cost_center, "against_voucher": d.sales_invoice, "against_voucher_type": "Sales Invoice" - }, inv.party_account_currency)) + }, inv.party_account_currency, item=inv)) gl_entries.append(self.get_gl_dict({ "account": self.accounts_receivable_credit, @@ -115,7 +121,7 @@ class InvoiceDiscounting(AccountsController): "cost_center": inv.cost_center, "against_voucher": d.sales_invoice, "against_voucher_type": "Sales Invoice" - }, ar_credit_account_currency)) + }, ar_credit_account_currency, item=inv)) make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No') diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 3b23aebd00f..da71acdb9da 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -85,7 +85,7 @@ class PaymentEntry(AccountsController): self.update_payment_schedule(cancel=1) self.set_payment_req_status() self.set_status() - + def set_payment_req_status(self): from erpnext.accounts.doctype.payment_request.payment_request import update_payment_req_status update_payment_req_status(self, None) @@ -503,7 +503,7 @@ class PaymentEntry(AccountsController): "against": against_account, "account_currency": self.party_account_currency, "cost_center": self.cost_center - }) + }, item=self) dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit" @@ -547,7 +547,7 @@ class PaymentEntry(AccountsController): "credit_in_account_currency": self.paid_amount, "credit": self.base_paid_amount, "cost_center": self.cost_center - }) + }, item=self) ) if self.payment_type in ("Receive", "Internal Transfer"): gl_entries.append( @@ -558,7 +558,7 @@ class PaymentEntry(AccountsController): "debit_in_account_currency": self.received_amount, "debit": self.base_received_amount, "cost_center": self.cost_center - }) + }, item=self) ) def add_deductions_gl_entries(self, gl_entries): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 3654f6ef032..e3a70991f89 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -452,7 +452,7 @@ class PurchaseInvoice(BuyingController): "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) def make_item_gl_entries(self, gl_entries): @@ -802,7 +802,7 @@ class PurchaseInvoice(BuyingController): "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) gl_entries.append( @@ -813,7 +813,7 @@ class PurchaseInvoice(BuyingController): "credit_in_account_currency": self.base_paid_amount \ if bank_account_currency==self.company_currency else self.paid_amount, "cost_center": self.cost_center - }, bank_account_currency) + }, bank_account_currency, item=self) ) def make_write_off_gl_entry(self, gl_entries): @@ -834,7 +834,7 @@ class PurchaseInvoice(BuyingController): "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) gl_entries.append( self.get_gl_dict({ @@ -844,7 +844,7 @@ class PurchaseInvoice(BuyingController): "credit_in_account_currency": self.base_write_off_amount \ if write_off_account_currency==self.company_currency else self.write_off_amount, "cost_center": self.cost_center or self.write_off_cost_center - }) + }, item=self) ) def make_gle_for_rounding_adjustment(self, gl_entries): @@ -863,8 +863,7 @@ class PurchaseInvoice(BuyingController): "debit_in_account_currency": self.rounding_adjustment, "debit": self.base_rounding_adjustment, "cost_center": self.cost_center or round_off_cost_center, - } - )) + }, item=self)) def on_cancel(self): super(PurchaseInvoice, self).on_cancel() diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 5218b740edd..fa175fc816e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -785,7 +785,7 @@ class SalesInvoice(SellingController): "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) def make_tax_gl_entries(self, gl_entries): @@ -802,7 +802,7 @@ class SalesInvoice(SellingController): tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))), "cost_center": tax.cost_center - }, account_currency) + }, account_currency, item=tax) ) def make_item_gl_entries(self, gl_entries): @@ -822,7 +822,7 @@ class SalesInvoice(SellingController): for gle in fixed_asset_gl_entries: gle["against"] = self.customer - gl_entries.append(self.get_gl_dict(gle)) + gl_entries.append(self.get_gl_dict(gle, item=item)) asset.db_set("disposal_date", self.posting_date) asset.set_status("Sold" if self.docstatus==1 else None) @@ -860,7 +860,7 @@ class SalesInvoice(SellingController): "against_voucher": self.return_against if cint(self.is_return) else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }) + }, item=self) ) gl_entries.append( self.get_gl_dict({ @@ -869,7 +869,7 @@ class SalesInvoice(SellingController): "against": self.customer, "debit": self.loyalty_amount, "remark": "Loyalty Points redeemed by the customer" - }) + }, item=self) ) def make_pos_gl_entries(self, gl_entries): @@ -890,7 +890,7 @@ class SalesInvoice(SellingController): "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) payment_mode_account_currency = get_account_currency(payment_mode.account) @@ -903,7 +903,7 @@ class SalesInvoice(SellingController): if payment_mode_account_currency==self.company_currency \ else payment_mode.amount, "cost_center": self.cost_center - }, payment_mode_account_currency) + }, payment_mode_account_currency, item=self) ) def make_gle_for_change_amount(self, gl_entries): @@ -921,7 +921,7 @@ class SalesInvoice(SellingController): "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) gl_entries.append( @@ -930,7 +930,7 @@ class SalesInvoice(SellingController): "against": self.customer, "credit": self.base_change_amount, "cost_center": self.cost_center - }) + }, item=self) ) else: frappe.throw(_("Select change amount account"), title="Mandatory Field") @@ -954,7 +954,7 @@ class SalesInvoice(SellingController): "against_voucher": self.return_against if cint(self.is_return) else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) gl_entries.append( self.get_gl_dict({ @@ -965,7 +965,7 @@ class SalesInvoice(SellingController): self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency else flt(self.write_off_amount, self.precision("write_off_amount"))), "cost_center": self.cost_center or self.write_off_cost_center or default_cost_center - }, write_off_account_currency) + }, write_off_account_currency, item=self) ) def make_gle_for_rounding_adjustment(self, gl_entries): @@ -982,8 +982,7 @@ class SalesInvoice(SellingController): "credit": flt(self.base_rounding_adjustment, self.precision("base_rounding_adjustment")), "cost_center": self.cost_center or round_off_cost_center, - } - )) + }, item=self)) def update_billing_status_in_dn(self, update_modified=True): updated_delivery_notes = [] diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 622bab6946f..07752e1e626 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -46,7 +46,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "default": frappe.defaults.get_user_default("year_end_date"), }, { - "fieldname":"cost_center", + "fieldname": "cost_center", "label": __("Cost Center"), "fieldtype": "Link", "options": "Cost Center", @@ -61,7 +61,13 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { } }, { - "fieldname":"finance_book", + "fieldname": "project", + "label": __("Project"), + "fieldtype": "Link", + "options": "Project" + }, + { + "fieldname": "finance_book", "label": __("Finance Book"), "fieldtype": "Link", "options": "Finance Book", @@ -97,7 +103,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { } erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{ + frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{ "fieldname": dimension["fieldname"], "label": __(dimension["label"]), "fieldtype": "Link", diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index d78324157a9..8bd4399e608 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -69,6 +69,10 @@ def get_data(filters): gl_entries_by_account = {} opening_balances = get_opening_balances(filters) + + #add filter inside list so that the query in financial_statements.py doesn't break + filters.project = [filters.project] + set_gl_entries_by_account(filters.company, filters.from_date, filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry)) @@ -102,6 +106,9 @@ def get_rootwise_opening_balances(filters, report_type): additional_conditions += """ and cost_center in (select name from `tabCost Center` where lft >= %s and rgt <= %s)""" % (lft, rgt) + if filters.project: + additional_conditions += " and project = %(project)s" + if filters.finance_book: fb_conditions = " AND finance_book = %(finance_book)s" if filters.include_default_book_entries: @@ -116,6 +123,7 @@ def get_rootwise_opening_balances(filters, report_type): "from_date": filters.from_date, "report_type": report_type, "year_start_date": filters.year_start_date, + "project": filters.project, "finance_book": filters.finance_book, "company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book') } diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index b8daf1fead9..d56549f7d90 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -512,7 +512,7 @@ class Asset(AccountsController): "credit": self.purchase_receipt_amount, "credit_in_account_currency": self.purchase_receipt_amount, "cost_center": self.cost_center - })) + }, item=self)) gl_entries.append(self.get_gl_dict({ "account": fixed_asset_account, @@ -522,7 +522,7 @@ class Asset(AccountsController): "debit": self.purchase_receipt_amount, "debit_in_account_currency": self.purchase_receipt_amount, "cost_center": self.cost_center - })) + }, item=self)) if gl_entries: from erpnext.accounts.general_ledger import make_gl_entries @@ -655,7 +655,7 @@ def get_asset_account(account_name, asset=None, asset_category=None, company=Non if asset: account = get_asset_category_account(account_name, asset=asset, asset_category = asset_category, company = company) - + if not asset and not account: account = get_asset_category_account(account_name, asset_category = asset_category, company = company) diff --git a/erpnext/education/doctype/fees/fees.py b/erpnext/education/doctype/fees/fees.py index f31003bf326..deb3968ecc7 100644 --- a/erpnext/education/doctype/fees/fees.py +++ b/erpnext/education/doctype/fees/fees.py @@ -97,14 +97,16 @@ class Fees(AccountsController): "debit_in_account_currency": self.grand_total, "against_voucher": self.name, "against_voucher_type": self.doctype - }) + }, item=self) + fee_gl_entry = self.get_gl_dict({ "account": self.income_account, "against": self.student, "credit": self.grand_total, "credit_in_account_currency": self.grand_total, "cost_center": self.cost_center - }) + }, item=self) + from erpnext.accounts.general_ledger import make_gl_entries make_gl_entries([student_gl_entries, fee_gl_entry], cancel=(self.docstatus == 2), update_outstanding="Yes", merge_entries=False) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index fefec01130a..4d97b0d0c72 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -115,8 +115,9 @@ class ExpenseClaim(AccountsController): "party_type": "Employee", "party": self.employee, "against_voucher_type": self.doctype, - "against_voucher": self.name - }) + "against_voucher": self.name, + "cost_center": self.cost_center + }, item=self) ) # expense entries @@ -127,8 +128,8 @@ class ExpenseClaim(AccountsController): "debit": data.sanctioned_amount, "debit_in_account_currency": data.sanctioned_amount, "against": self.employee, - "cost_center": self.cost_center - }) + "cost_center": data.cost_center + }, item=data) ) for data in self.advances: @@ -156,7 +157,7 @@ class ExpenseClaim(AccountsController): "credit": self.grand_total, "credit_in_account_currency": self.grand_total, "against": self.employee - }) + }, item=self) ) gl_entry.append( @@ -169,7 +170,7 @@ class ExpenseClaim(AccountsController): "debit_in_account_currency": self.grand_total, "against_voucher": self.name, "against_voucher_type": self.doctype, - }) + }, item=self) ) return gl_entry @@ -186,7 +187,7 @@ class ExpenseClaim(AccountsController): "cost_center": self.cost_center, "against_voucher_type": self.doctype, "against_voucher": self.name - }) + }, item=tax) ) def validate_account_details(self): diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json index b23fb6af0fe..518e0aeefac 100644 --- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json +++ b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json @@ -10,6 +10,22 @@ "docstatus": 0, "doctype": "DocType", "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "expense_date", + "column_break_2", + "expense_type", + "default_account", + "section_break_4", + "description", + "section_break_6", + "amount", + "column_break_8", + "sanctioned_amount", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break" + ], "fields": [ { "allow_bulk_edit": 0, @@ -348,6 +364,21 @@ "translatable": 0, "unique": 0, "width": "150px" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" } ], "has_web_view": 0, @@ -359,12 +390,12 @@ "is_submittable": 0, "issingle": 0, "istable": 1, - "max_attachments": 0, - "modified": "2019-06-10 08:41:36.122565", + "links": [], + "modified": "2020-05-11 18:54:35.601592", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim Detail", - "owner": "harshada@webnotestech.com", + "owner": "Administrator", "permissions": [], "quick_entry": 0, "read_only": 0, diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json index 1b1889b205f..f6a1e1bfe25 100644 --- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json +++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json @@ -7,14 +7,16 @@ "engine": "InnoDB", "field_order": [ "account_head", - "cost_center", "col_break1", "rate", "description", "section_break_6", "tax_amount", "column_break_8", - "total" + "total", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break" ], "fields": [ { @@ -90,10 +92,20 @@ { "fieldname": "column_break_8", "fieldtype": "Column Break" + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" } ], "istable": 1, - "modified": "2019-06-20 12:01:33.919555", + "links": [], + "modified": "2020-05-11 19:01:26.611758", "modified_by": "Administrator", "module": "HR", "name": "Expense Taxes and Charges", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 30ec5be4902..5517cbc9a80 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -625,7 +625,7 @@ 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 erpnext.patches.v12_0.update_ewaybill_field_position -erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes +erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes #2020-05-11 erpnext.patches.v11_1.set_status_for_material_request_type_manufacture erpnext.patches.v12_0.move_plaid_settings_to_doctype execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_link') diff --git a/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py b/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py index b71ea665943..657decfed23 100644 --- a/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py +++ b/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py @@ -20,7 +20,8 @@ def execute(): else: insert_after_field = 'accounting_dimensions_section' - for doctype in ["Subscription Plan", "Subscription", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item"]: + for doctype in ["Subscription Plan", "Subscription", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", + "Expense Claim Detail", "Expense Taxes and Charges"]: field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname}) From 2fa841821a028b65f62d21e774f9e7be24a4ac9a Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 15 May 2020 14:31:19 +0530 Subject: [PATCH 481/929] fix: add tests for set_status --- .../doctype/purchase_invoice/purchase_invoice.py | 2 +- .../purchase_invoice/test_purchase_invoice.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 978898a54b2..b75c1701922 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1002,7 +1002,7 @@ class PurchaseInvoice(BuyingController): elif outstanding_amount > 0 and due_date >= nowdate: self.status = "Unpaid" #Check if outstanding amount is 0 due to debit note issued against invoice - elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): + elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): self.status = "Debit Note Issued" elif self.is_return == 1: self.status = "Return" diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index e41ad428469..61700050614 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -86,6 +86,8 @@ class TestPurchaseInvoice(unittest.TestCase): pe.submit() pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name) + pi_doc.load_from_db() + self.assertTrue(pi_doc.status, "Paid") self.assertRaises(frappe.LinkExistsError, pi_doc.cancel) unlink_payment_on_cancel_of_invoice() @@ -203,7 +205,9 @@ class TestPurchaseInvoice(unittest.TestCase): pi.insert() pi.submit() + pi.load_from_db() + self.assertTrue(pi.status, "Unpaid") self.check_gle_for_pi(pi.name) def check_gle_for_pi(self, pi): @@ -234,6 +238,9 @@ class TestPurchaseInvoice(unittest.TestCase): pi = frappe.copy_doc(test_records[0]) pi.insert() + pi.load_from_db() + + self.assertTrue(pi.status, "Draft") pi.naming_series = 'TEST-' self.assertRaises(frappe.CannotChangeConstantError, pi.save) @@ -248,6 +255,8 @@ class TestPurchaseInvoice(unittest.TestCase): pi.get("taxes").pop(1) pi.insert() pi.submit() + pi.load_from_db() + self.assertTrue(pi.status, "Unpaid") gl_entries = frappe.db.sql("""select account, debit, credit from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s @@ -599,6 +608,11 @@ class TestPurchaseInvoice(unittest.TestCase): # return entry pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1) + pi.load_from_db() + self.assertTrue(pi.status, "Debit Note Issued") + pi1.load_from_db() + self.assertTrue(pi1.status, "Return") + actual_qty_2 = get_qty_after_transaction() self.assertEqual(actual_qty_1 - 2, actual_qty_2) @@ -771,6 +785,8 @@ class TestPurchaseInvoice(unittest.TestCase): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1) + pi.load_from_db() + self.assertTrue(pi.status, "Return") outstanding_amount = get_outstanding_amount(pi.doctype, pi.name, "Creditors - _TC", pi.supplier, "Supplier") From 725684a0c3857d9b60c75552ef8d0daee6bf4f24 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 15 May 2020 16:39:02 +0530 Subject: [PATCH 482/929] fix: update remark on submitting payment entry --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index da71acdb9da..ab7aa15e3dd 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -448,8 +448,6 @@ class PaymentEntry(AccountsController): frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction")) def set_remarks(self): - if self.remarks: return - if self.payment_type=="Internal Transfer": remarks = [_("Amount {0} {1} transferred from {2} to {3}") .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)] From 52890341d569c1b0c1a6c5bc18d92125672dfd0a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 14 May 2020 17:15:16 +0530 Subject: [PATCH 483/929] fix: Get basic and hra component from db, not from cache (cherry picked from commit 10df3d5081c54d5ee70bdf69c81c3d70acb35169) --- erpnext/regional/india/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index e518c14a8a0..d22aa1eebb8 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -236,8 +236,7 @@ def get_tax_template_for_sez(party_details, master_doctype, company, party_type) def calculate_annual_eligible_hra_exemption(doc): - basic_component = frappe.get_cached_value('Company', doc.company, "basic_component") - hra_component = frappe.get_cached_value('Company', doc.company, "hra_component") + basic_component, hra_component = frappe.db.get_value('Company', doc.company, ["basic_component", "hra_component"]) if not (basic_component and hra_component): frappe.throw(_("Please mention Basic and HRA component in Company")) annual_exemption, monthly_exemption, hra_amount = 0, 0, 0 From 78f69e448b361d134a76aef663997d8490e702c7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 14 May 2020 17:18:29 +0530 Subject: [PATCH 484/929] fix: Added submit permission in employee other income (cherry picked from commit 200f80c3d368884000ebd8b5942e1e9ea28d0301) --- .../employee_other_income.json | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.json b/erpnext/hr/doctype/employee_other_income/employee_other_income.json index 2dd6c10988d..8abfe1e93a8 100644 --- a/erpnext/hr/doctype/employee_other_income/employee_other_income.json +++ b/erpnext/hr/doctype/employee_other_income/employee_other_income.json @@ -76,25 +76,15 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-19 18:06:45.361830", + "modified": "2020-05-14 17:17:38.883126", "modified_by": "Administrator", "module": "HR", "name": "Employee Other Income", "owner": "Administrator", "permissions": [ { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { + "amend": 1, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -104,9 +94,12 @@ "report": 1, "role": "HR Manager", "share": 1, + "submit": 1, "write": 1 }, { + "amend": 1, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -116,9 +109,12 @@ "report": 1, "role": "HR User", "share": 1, + "submit": 1, "write": 1 }, { + "amend": 1, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -128,6 +124,7 @@ "report": 1, "role": "Employee", "share": 1, + "submit": 1, "write": 1 } ], From 79b691fe182470e58d9282bb5fdd252192ff96e4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 15 May 2020 19:23:16 +0530 Subject: [PATCH 485/929] fix: Better validation message for group accounts (#21726) --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 512e348c8bf..7358a31469b 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -115,8 +115,8 @@ class GLEntry(Document): from tabAccount where name=%s""", self.account, as_dict=1)[0] if ret.is_group==1: - frappe.throw(_("{0} {1}: Account {2} cannot be a Group") - .format(self.voucher_type, self.voucher_no, self.account)) + frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in + transactions''').format(self.voucher_type, self.voucher_no, self.account)) if ret.docstatus==2: frappe.throw(_("{0} {1}: Account {2} is inactive") From a52fe009cc2f63ab513d5432d6f8148add6ea8e1 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 15 May 2020 19:35:35 +0530 Subject: [PATCH 486/929] fix: user not able to view product (#21697) --- erpnext/shopping_cart/product_info.py | 8 +++++--- erpnext/stock/doctype/item/item.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index a7da09cb808..21ee335125b 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -10,14 +10,16 @@ from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status @frappe.whitelist(allow_guest=True) -def get_product_info_for_website(item_code): +def get_product_info_for_website(item_code, skip_quotation_creation=False): """get product price / stock info for website""" cart_settings = get_shopping_cart_settings() if not cart_settings.enabled: return frappe._dict() - cart_quotation = _get_cart_quotation() + cart_quotation = frappe._dict() + if not skip_quotation_creation: + cart_quotation = _get_cart_quotation() price = get_price( item_code, @@ -51,7 +53,7 @@ def get_product_info_for_website(item_code): def set_product_info_for_website(item): """set product price uom for website""" - product_info = get_product_info_for_website(item.item_code) + product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True) if product_info: item.update(product_info) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index d7edd3ab51f..f5ffe242a1f 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -467,7 +467,7 @@ class Item(WebsiteGenerator): def set_shopping_cart_data(self, context): from erpnext.shopping_cart.product_info import get_product_info_for_website - context.shopping_cart = get_product_info_for_website(self.name) + context.shopping_cart = get_product_info_for_website(self.name, skip_quotation_creation=True) def add_default_uom_in_conversion_factor_table(self): uom_conv_list = [d.uom for d in self.get("uoms")] From 88c2ba54abc4357e4cff623447e8fad6d140b5a1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 23 Feb 2020 21:03:47 +0530 Subject: [PATCH 487/929] fix: Excel support and UX fixes for chart of accounts importer --- .../chart_of_accounts_importer.js | 58 ++-- .../chart_of_accounts_importer.json | 267 ++++-------------- .../chart_of_accounts_importer.py | 166 +++++++++-- 3 files changed, 235 insertions(+), 256 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 40a97ae295e..93a4180a30a 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 @@ -2,6 +2,12 @@ frappe.ui.form.on('Chart of Accounts Importer', { onload: function (frm) { frm.set_value("company", ""); frm.set_value("import_file", ""); + + frappe.db.get_value('File', {'attached_to_doctype': 'Chart of Accounts Importer'}, 'file_url', (r) => { + if (r) { + frm.set_value('import_file', r.file_url); + } + }); }, refresh: function (frm) { // disable default save @@ -17,17 +23,15 @@ frappe.ui.form.on('Chart of Accounts Importer', { if (frm.page && frm.page.show_import_button) { create_import_button(frm); } + }, - // show download template button when company is properly selected - if(frm.doc.company) { - // download the csv template file - frm.add_custom_button(__("Download template"), function () { - let get_template_url = 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template'; - open_url_post(frappe.request.url, { cmd: get_template_url, doctype: frm.doc.doctype }); - }); - } else { - frm.set_value("import_file", ""); - } + download_template: function(frm) { + open_url_post( + '/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template', + { + file_type: frm.doc.file_type + } + ) }, import_file: function (frm) { @@ -41,21 +45,23 @@ frappe.ui.form.on('Chart of Accounts Importer', { }, company: function (frm) { - // validate that no Gl Entry record for the company exists. - frappe.call({ - method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_company", - args: { - company: frm.doc.company - }, - callback: function(r) { - if(r.message===false) { - frm.set_value("company", ""); - frappe.throw(__("Transactions against the company already exist! ")); - } else { - frm.trigger("refresh"); + if (frm.doc.company) { + // validate that no Gl Entry record for the company exists. + frappe.call({ + method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_company", + args: { + company: frm.doc.company + }, + callback: function(r) { + if(r.message===false) { + frm.set_value("company", ""); + frappe.throw(__("Transactions against the company already exist! ")); + } else { + frm.trigger("refresh"); + } } - } - }); + }); + } } }); @@ -87,6 +93,7 @@ var create_import_button = function(frm) { freeze: true, freeze_message: __("Creating Accounts..."), callback: function(r) { + console.log(r, '###########'); if(!r.exc) { clearInterval(frm.page["interval"]); frm.page.set_indicator(__('Import Successfull'), 'blue'); @@ -118,7 +125,8 @@ var generate_tree_preview = function(frm) { args: { file_name: frm.doc.import_file, parent: parent, - doctype: 'Chart of Accounts Importer' + doctype: 'Chart of Accounts Importer', + file_type: frm.doc.file_type }, onclick: function(node) { parent = node.value; diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json index d544e69231e..555413b31fa 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json @@ -1,226 +1,83 @@ { - "allow_copy": 1, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2019-02-01 12:24:34.761380", - "custom": 0, + "actions": [], + "allow_copy": 1, + "creation": "2019-02-01 12:24:34.761380", "description": "Import Chart of Accounts from a csv file", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Other", - "editable_grid": 1, - "engine": "InnoDB", + "doctype": "DocType", + "document_type": "Other", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "file_type", + "download_template", + "import_file_section", + "import_file", + "chart_preview", + "chart_tree" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "import_file_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, - "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": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", + "depends_on": "eval:!doc.__islocal", "fieldname": "import_file", "fieldtype": "Attach", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Attach custom Chart of Accounts file", - "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": "Attach custom Chart of Accounts file" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "chart_preview", - "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": "Chart Preview", - "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": "Section Break", + "label": "Chart Preview" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "chart_tree", "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": "Chart Tree", - "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": "Chart Tree" + }, + { + "depends_on": "company", + "fieldname": "download_template", + "fieldtype": "Button", + "label": "Download Template" + }, + { + "depends_on": "company", + "fieldname": "file_type", + "fieldtype": "Select", + "label": "File Type", + "options": "CSV\nExcel" } - ], - "has_web_view": 0, - "hide_heading": 1, - "hide_toolbar": 1, - "idx": 0, - "image_view": 0, - "in_create": 1, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2019-02-04 23:10:30.136807", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Chart of Accounts Importer", - "name_case": "", - "owner": "Administrator", + ], + "hide_toolbar": 1, + "in_create": 1, + "issingle": 1, + "links": [], + "modified": "2020-02-22 19:32:33.349075", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Chart of Accounts Importer", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "read": 1, + "role": "Accounts Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 1, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "read_only": 1, + "sort_order": "DESC" } \ 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 f496280317a..18fb75a24cd 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 @@ -4,18 +4,28 @@ from __future__ import unicode_literals from functools import reduce -import frappe, csv +import frappe, csv, os from frappe import _ -from frappe.utils import cstr +from frappe.utils import cstr, cint from frappe.model.document import Document from frappe.utils.csvutils import UnicodeWriter from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts, build_tree_from_json +from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file, read_xls_file_from_attached_file class ChartofAccountsImporter(Document): pass @frappe.whitelist() def validate_company(company): + parent_company, allow_account_creation_against_child_company = frappe.db.get_value('Company', + {'name': company}, ['parent_company', + 'allow_account_creation_against_child_company']) + + if parent_company and (not allow_account_creation_against_child_company): + frappe.throw(_("""{0} is child company. Please import accounts in parent company + or enable {1} in company master""").format(frappe.bold(company), + frappe.bold('Allow Account Creation Against Child Company'))) + if frappe.db.get_all('GL Entry', {"company": company}, "name", limit=1): return False @@ -25,42 +35,85 @@ def import_coa(file_name, company): unset_existing_data(company) # create accounts - forest = build_forest(generate_data_from_csv(file_name)) + file_doc, extension = get_file(file_name) + + if extension == 'csv': + data = generate_data_from_csv(file_doc) + else: + data = generate_data_from_excel(file_doc, extension) + + forest = build_forest(data) create_charts(company, custom_chart=forest) # trigger on_update for company to reset default accounts set_default_accounts(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") +def get_file(file_name): + file_doc = frappe.get_doc("File", {"file_url": file_name}) + parts = file_doc.get_extension() + extension = parts[1] + extension = extension.lstrip(".") + + if extension not in ('csv', 'xlsx', 'xls'): + frappe.throw("Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload") + + return file_doc, extension + +def generate_data_from_csv(file_doc, as_dict=False): + ''' read csv file and return the generated nested tree ''' - file_doc = frappe.get_doc('File', {"file_url": file_name}) file_path = file_doc.get_full_path() data = [] with open(file_path, 'r') as in_file: csv_reader = list(csv.reader(in_file)) - headers = csv_reader[1][1:] - del csv_reader[0:2] # delete top row and headers row + headers = csv_reader[0] + del csv_reader[0] # delete top row and headers row for row in csv_reader: if as_dict: - data.append({frappe.scrub(header): row[index+1] for index, header in enumerate(headers)}) + data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)}) else: - if not row[2]: row[2] = row[1] - data.append(row[1:]) + if not row[1]: row[1] = row[0] + data.append(row) # convert csv data return data +def generate_data_from_excel(file_doc, extension, as_dict=False): + content = file_doc.get_content() + + if extension == "xlsx": + rows = read_xlsx_file_from_attached_file(fcontent=content) + elif extension == "xls": + rows = read_xls_file_from_attached_file(content) + + data = [] + headers = rows[0] + del rows[0] + + for row in rows: + if as_dict: + data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)}) + else: + if not row[1]: row[1] = row[0] + data.append(row) + + return data + @frappe.whitelist() -def get_coa(doctype, parent, is_root=False, file_name=None): +def get_coa(doctype, parent, file_type, is_root=False, file_name=None): ''' called by tree view (to fetch node's children) ''' + file_doc, extension = get_file(file_name) parent = None if parent==_('All Accounts') else parent - forest = build_forest(generate_data_from_csv(file_name)) + + if extension == 'csv': + data = generate_data_from_csv(file_doc) + else: + data = generate_data_from_excel(file_doc, extension) + + forest = build_forest(data) accounts = build_tree_from_json("", chart_data=forest) # returns alist of dict in a tree render-able form # filter out to show data for the selected node only @@ -114,7 +167,7 @@ def build_forest(data): error_messages.append("Row {0}: Please enter Account Name".format(line_no)) charts_map[account_name] = {} - if is_group == 1: charts_map[account_name]["is_group"] = is_group + if cint(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 @@ -132,24 +185,59 @@ def build_forest(data): return out +def build_response_as_excel(writer): + filename = frappe.generate_hash("", 10) + with open(filename, 'wb') as f: + f.write(cstr(writer.getvalue()).encode('utf-8')) + f = open(filename) + reader = csv.reader(f) + + from frappe.utils.xlsxutils import make_xlsx + xlsx_file = make_xlsx(reader, "Chart Of Accounts Importer Template") + + f.close() + os.remove(filename) + + # write out response as a xlsx type + frappe.response['filename'] = 'coa_importer_template.xlsx' + frappe.response['filecontent'] = xlsx_file.getvalue() + frappe.response['type'] = 'binary' + @frappe.whitelist() -def download_template(): +def download_template(file_type): data = frappe._dict(frappe.local.form_dict) + fields = ["Account Name", "Parent Account", "Account Number", "Is Group", "Account Type", "Root Type"] writer = UnicodeWriter() - writer.writerow([_('Chart of Accounts Template')]) - writer.writerow([_("Column Labels : ")] + fields) - writer.writerow([_("Start entering data from here : ")]) + writer.writerow(fields) - # download csv file - frappe.response['result'] = cstr(writer.getvalue()) - frappe.response['type'] = 'csv' - frappe.response['doctype'] = data.get('doctype') + for root_type in get_root_types(): + writer.writerow(['', '', '', 1, '', root_type]) + + for account in get_mandatory_group_accounts(): + writer.writerow(['', '', '', 1, account, 'Asset']) + + for account_type in get_mandatory_account_types(): + writer.writerow(['', '', '', 0, account_type.get('account_type'), account_type.get('root_type')]) + + if file_type == 'CSV': + # download csv file + frappe.response['result'] = cstr(writer.getvalue()) + frappe.response['type'] = 'csv' + frappe.response['doctype'] = 'Chart of Accounts Importer' + else: + build_response_as_excel(writer) @frappe.whitelist() def validate_accounts(file_name): - accounts = generate_data_from_csv(file_name, as_dict=True) + + file_doc, extension = get_file(file_name) + + if extension == 'csv': + accounts = generate_data_from_csv(file_doc, as_dict=True) + else: + accounts = generate_data_from_excel(file_doc, extension, as_dict=True) accounts_dict = {} for account in accounts: @@ -174,12 +262,38 @@ def validate_root(accounts): for account in roots: 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"): + elif account.get("root_type") not in get_root_types() 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 get_root_types(): + return ('Asset', 'Liability', 'Expense', 'Income', 'Equity') + +def get_report_type(root_type): + if root_type in ('Asset', 'Liability', 'Equity'): + return 'Balance Sheet' + else: + return 'Profit and Loss' + +def get_mandatory_group_accounts(): + return ('Bank', 'Cash', 'Stock') + +def get_mandatory_account_types(): + return [ + {'account_type': 'Cost of Goods Sold', 'root_type': 'Expense'}, + {'account_type': 'Depreciation', 'root_type': 'Expense'}, + {'account_type': 'Fixed Asset', 'root_type': 'Asset'}, + {'account_type': 'Payable', 'root_type': 'Liability'}, + {'account_type': 'Receivable', 'root_type': 'Asset'}, + {'account_type': 'Stock Adjustment', 'root_type': 'Expense'}, + {'account_type': 'Bank', 'root_type': 'Asset'}, + {'account_type': 'Cash', 'root_type': 'Asset'}, + {'account_type': 'Stock', 'root_type': 'Asset'} + ] + + 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'] == 1] From 8ff718249eff94fae5741a49086475be887d817a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 24 Feb 2020 12:06:25 +0530 Subject: [PATCH 488/929] fix: Linting Errors --- .../chart_of_accounts_importer/chart_of_accounts_importer.js | 3 +-- 1 file changed, 1 insertion(+), 2 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 93a4180a30a..92358c9af27 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 @@ -31,7 +31,7 @@ frappe.ui.form.on('Chart of Accounts Importer', { { file_type: frm.doc.file_type } - ) + ); }, import_file: function (frm) { @@ -93,7 +93,6 @@ var create_import_button = function(frm) { freeze: true, freeze_message: __("Creating Accounts..."), callback: function(r) { - console.log(r, '###########'); if(!r.exc) { clearInterval(frm.page["interval"]); frm.page.set_indicator(__('Import Successfull'), 'blue'); From 838ed777977b43334e5b0de10bf73a29b669cd9d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 24 Feb 2020 17:36:15 +0530 Subject: [PATCH 489/929] fix: Blank chart preview --- .../chart_of_accounts_importer.js | 6 ------ 1 file changed, 6 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 92358c9af27..189b2f9210f 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 @@ -2,12 +2,6 @@ frappe.ui.form.on('Chart of Accounts Importer', { onload: function (frm) { frm.set_value("company", ""); frm.set_value("import_file", ""); - - frappe.db.get_value('File', {'attached_to_doctype': 'Chart of Accounts Importer'}, 'file_url', (r) => { - if (r) { - frm.set_value('import_file', r.file_url); - } - }); }, refresh: function (frm) { // disable default save From 101612e5990634148dc890bfa14dba3ebec5e79f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 25 Feb 2020 15:07:05 +0530 Subject: [PATCH 490/929] fix: Added template types for download --- .../chart_of_accounts_importer.json | 11 +++- .../chart_of_accounts_importer.py | 64 +++++++++++++++---- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json index 555413b31fa..b4c4fce3511 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json @@ -10,6 +10,7 @@ "field_order": [ "company", "file_type", + "template_type", "download_template", "import_file_section", "import_file", @@ -57,13 +58,21 @@ "fieldtype": "Select", "label": "File Type", "options": "CSV\nExcel" + }, + { + "depends_on": "company", + "fieldname": "template_type", + "fieldtype": "Select", + "label": "Template Type", + "options": "\nBlank Template\nSample Template", + "reqd": 1 } ], "hide_toolbar": 1, "in_create": 1, "issingle": 1, "links": [], - "modified": "2020-02-22 19:32:33.349075", + "modified": "2020-02-24 19:07:52.670868", "modified_by": "Administrator", "module": "Accounts", "name": "Chart of Accounts Importer", 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 18fb75a24cd..12e147192cb 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 @@ -204,22 +204,10 @@ def build_response_as_excel(writer): frappe.response['type'] = 'binary' @frappe.whitelist() -def download_template(file_type): +def download_template(file_type, template_type): data = frappe._dict(frappe.local.form_dict) - fields = ["Account Name", "Parent Account", "Account Number", "Is Group", "Account Type", "Root Type"] - writer = UnicodeWriter() - - writer.writerow(fields) - - for root_type in get_root_types(): - writer.writerow(['', '', '', 1, '', root_type]) - - for account in get_mandatory_group_accounts(): - writer.writerow(['', '', '', 1, account, 'Asset']) - - for account_type in get_mandatory_account_types(): - writer.writerow(['', '', '', 0, account_type.get('account_type'), account_type.get('root_type')]) + writer = get_template(template_type) if file_type == 'CSV': # download csv file @@ -229,6 +217,54 @@ def download_template(file_type): else: build_response_as_excel(writer) +def get_template(template_type): + + fields = ["Account Name", "Parent Account", "Account Number", "Is Group", "Account Type", "Root Type"] + writer = UnicodeWriter() + writer.writerow(fields) + + if template_type == 'Blank Template': + for root_type in get_root_types(): + writer.writerow(['', '', '', 1, '', root_type]) + + for account in get_mandatory_group_accounts(): + writer.writerow(['', '', '', 1, account, 'Asset']) + + for account_type in get_mandatory_account_types(): + writer.writerow(['', '', '', 0, account_type.get('account_type'), account_type.get('root_type')]) + else: + writer = get_sample_template(writer) + + return writer + +def get_sample_template(writer): + template = [ + ['Account Name', 'Parent Account', 'Account Number', 'Is Group', 'Account Type', 'Root Type'], + ['Application Of Funds(Assets)', '', '', 1, '', 'Asset'], + ['Sources Of Funds(Liabilities)', '', '', 1, '', 'Liability'], + ['Equity', '', '', 1, '', 'Equity'], + ['Expenses', '', '', 1, '', 'Expense'], + ['Income', '', '', 1, '', 'Income'], + ['Bank Accounts', 'Application Of Funds(Assets)', '', 1, 'Bank', 'Asset'], + ['Cash In Hand', 'Application Of Funds(Assets)', '', 1, 'Cash', 'Asset'], + ['Stock Assets', 'Application Of Funds(Assets)', '', 1, 'Stock', 'Asset'], + ['Cost Of Goods Sold', 'Expenses', '', 0, 'Cost of Goods Sold', 'Expense'], + ['Asset Depreciation', 'Expenses', '', 0, 'Depreciation', 'Expense'], + ['Fixed Assets', 'Application Of Funds(Assets)', '', 0, 'Fixed Asset', 'Asset'], + ['Accounts Payable', 'Sources Of Funds(Liabilities)', '', 0, 'Payable', 'Liability'], + ['Accounts Receivable', 'Application Of Funds(Assets)', '', 1, 'Receivable', 'Asset'], + ['Stock Expenses', 'Expenses', '', 0, 'Stock Adjustment', 'Expense'], + ['Sample Bank', 'Bank Accounts', '', 0, 'Bank', 'Asset'], + ['Cash', 'Cash In Hand', '', 0, 'Cash', 'Asset'], + ['Stores', 'Stock Asset', '', 0, 'Stock', 'Asset'], + ] + + for row in template: + writer.writerow(row) + + return writer + + @frappe.whitelist() def validate_accounts(file_name): From 317b53a8b68a9c4ea0b52ea5e08e69ac6022d5ac Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 25 Feb 2020 15:07:31 +0530 Subject: [PATCH 491/929] fix: Description on template selection --- .../chart_of_accounts_importer.js | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 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 189b2f9210f..049c15d6650 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 @@ -20,14 +20,32 @@ frappe.ui.form.on('Chart of Accounts Importer', { }, download_template: function(frm) { + + if (!frm.doc.template_type) { + frappe.throw(__('Please select Template Type to download template')); + } + open_url_post( '/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template', { - file_type: frm.doc.file_type + file_type: frm.doc.file_type, + template_type: frm.doc.template_type } ); }, + template_type: function(frm) { + if (frm.doc.template_type == 'Sample Template') { + frm.set_df_property('template_type', 'description', + `The Sample Template contains all the required accounts pre filled in the template. + You can add more accounts or change existing accounts in the template as per your choice.`) + } else { + frm.set_df_property('template_type', 'description', + `The Blank Template contains just the account type and root type required to build the Chart + of Accounts. Please enter the account names and add more rows as per your requirement.`) + } + }, + import_file: function (frm) { if (!frm.doc.import_file) { frm.page.set_indicator(""); @@ -49,7 +67,8 @@ frappe.ui.form.on('Chart of Accounts Importer', { callback: function(r) { if(r.message===false) { frm.set_value("company", ""); - frappe.throw(__("Transactions against the company already exist! ")); + frappe.throw(__(`Transactions against the company already exist! + Chart Of accounts can be imported for company with no transactions`)); } else { frm.trigger("refresh"); } From 39790f05e7c1ec752d9c499a041a6a52ff500b95 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 25 Feb 2020 17:11:33 +0530 Subject: [PATCH 492/929] fix: Linting fixes --- .../chart_of_accounts_importer/chart_of_accounts_importer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 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 049c15d6650..c310e26a656 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 @@ -38,11 +38,11 @@ frappe.ui.form.on('Chart of Accounts Importer', { if (frm.doc.template_type == 'Sample Template') { frm.set_df_property('template_type', 'description', `The Sample Template contains all the required accounts pre filled in the template. - You can add more accounts or change existing accounts in the template as per your choice.`) + You can add more accounts or change existing accounts in the template as per your choice.`); } else { frm.set_df_property('template_type', 'description', `The Blank Template contains just the account type and root type required to build the Chart - of Accounts. Please enter the account names and add more rows as per your requirement.`) + of Accounts. Please enter the account names and add more rows as per your requirement.`); } }, From ec19926a97d0967dca0c9ccecc020c52ca5a15bd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 29 Feb 2020 20:23:39 +0530 Subject: [PATCH 493/929] Move logic for download template to dialog --- .../chart_of_accounts_importer.js | 71 +++++++++++++------ .../chart_of_accounts_importer.json | 27 +------ .../chart_of_accounts_importer.py | 52 +++++++------- 3 files changed, 79 insertions(+), 71 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 c310e26a656..0b7cff3d63c 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 @@ -20,30 +20,57 @@ frappe.ui.form.on('Chart of Accounts Importer', { }, download_template: function(frm) { + var d = new frappe.ui.Dialog({ + title: __("Download Template"), + fields: [ + { + label : "File Type", + fieldname: "file_type", + fieldtype: "Select", + reqd: 1, + options: ["Excel", "CSV"] + }, + { + label: "Template Type", + fieldname: "template_type", + fieldtype: "Select", + reqd: 1, + options: ["Sample Template", "Blank Template"], + change: () => { + let template_type = d.get_value('template_type'); - if (!frm.doc.template_type) { - frappe.throw(__('Please select Template Type to download template')); - } + if (template_type === "Sample Template") { + d.set_df_property('template_type', 'description', + `The Sample Template contains all the required accounts pre filled in the template. + You can add more accounts or change existing accounts in the template as per your choice.`); + } else { + d.set_df_property('template_type', 'description', + `The Blank Template contains just the account type and root type required to build the Chart + of Accounts. Please enter the account names and add more rows as per your requirement.`); + } + } + } + ], + primary_action: function() { + var data = d.get_values(); - open_url_post( - '/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template', - { - file_type: frm.doc.file_type, - template_type: frm.doc.template_type - } - ); - }, + if (!data.template_type) { + frappe.throw(__('Please select Template Type to download template')); + } - template_type: function(frm) { - if (frm.doc.template_type == 'Sample Template') { - frm.set_df_property('template_type', 'description', - `The Sample Template contains all the required accounts pre filled in the template. - You can add more accounts or change existing accounts in the template as per your choice.`); - } else { - frm.set_df_property('template_type', 'description', - `The Blank Template contains just the account type and root type required to build the Chart - of Accounts. Please enter the account names and add more rows as per your requirement.`); - } + open_url_post( + '/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template', + { + file_type: data.file_type, + template_type: data.template_type + } + ); + + d.hide(); + }, + primary_action_label: __('Download') + }); + d.show(); }, import_file: function (frm) { @@ -96,7 +123,7 @@ var validate_csv_data = function(frm) { }; var create_import_button = function(frm) { - frm.page.set_primary_action(__("Start Import"), function () { + frm.page.set_primary_action(__("Import"), function () { frappe.call({ method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa", args: { diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json index b4c4fce3511..ee095ac386a 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json @@ -9,10 +9,7 @@ "engine": "InnoDB", "field_order": [ "company", - "file_type", - "template_type", "download_template", - "import_file_section", "import_file", "chart_preview", "chart_tree" @@ -26,11 +23,7 @@ "options": "Company" }, { - "fieldname": "import_file_section", - "fieldtype": "Section Break" - }, - { - "depends_on": "eval:!doc.__islocal", + "depends_on": "company", "fieldname": "import_file", "fieldtype": "Attach", "label": "Attach custom Chart of Accounts file" @@ -51,28 +44,13 @@ "fieldname": "download_template", "fieldtype": "Button", "label": "Download Template" - }, - { - "depends_on": "company", - "fieldname": "file_type", - "fieldtype": "Select", - "label": "File Type", - "options": "CSV\nExcel" - }, - { - "depends_on": "company", - "fieldname": "template_type", - "fieldtype": "Select", - "label": "Template Type", - "options": "\nBlank Template\nSample Template", - "reqd": 1 } ], "hide_toolbar": 1, "in_create": 1, "issingle": 1, "links": [], - "modified": "2020-02-24 19:07:52.670868", + "modified": "2020-02-28 08:49:11.422846", "modified_by": "Administrator", "module": "Accounts", "name": "Chart of Accounts Importer", @@ -88,5 +66,6 @@ ], "quick_entry": 1, "read_only": 1, + "sort_field": "modified", "sort_order": "DESC" } \ 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 12e147192cb..4100699fe39 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 @@ -22,9 +22,9 @@ def validate_company(company): 'allow_account_creation_against_child_company']) if parent_company and (not allow_account_creation_against_child_company): - frappe.throw(_("""{0} is child company. Please import accounts in parent company + frappe.throw(_("""{0} is a child company. Please import accounts against parent company or enable {1} in company master""").format(frappe.bold(company), - frappe.bold('Allow Account Creation Against Child Company'))) + frappe.bold('Allow Account Creation Against Child Company')), title='Wrong Company') if frappe.db.get_all('GL Entry', {"company": company}, "name", limit=1): return False @@ -102,7 +102,7 @@ def generate_data_from_excel(file_doc, extension, as_dict=False): return data @frappe.whitelist() -def get_coa(doctype, parent, file_type, is_root=False, file_name=None): +def get_coa(doctype, parent, is_root=False, file_name=None): ''' called by tree view (to fetch node's children) ''' file_doc, extension = get_file(file_name) @@ -144,15 +144,18 @@ def build_forest(data): # returns the path of any node in list format def return_parent(data, child): + from frappe import _ + for row in data: account_name, parent_account = row[0:2] if parent_account == account_name == child: return [parent_account] elif account_name == child: parent_account_list = return_parent(data, parent_account) - if not parent_account_list and parent_account: - frappe.throw(_("The parent account {0} does not exists") - .format(parent_account)) + if not parent_account_list: + frappe.throw(_("The parent account {0} does not exists in the uploaded template").format( + frappe.bold(parent_account))) + return [child] + parent_account_list charts_map, paths = {}, [] @@ -228,7 +231,7 @@ def get_template(template_type): writer.writerow(['', '', '', 1, '', root_type]) for account in get_mandatory_group_accounts(): - writer.writerow(['', '', '', 1, account, 'Asset']) + writer.writerow(['', '', '', 1, account, "Asset"]) for account_type in get_mandatory_account_types(): writer.writerow(['', '', '', 0, account_type.get('account_type'), account_type.get('root_type')]) @@ -239,24 +242,23 @@ def get_template(template_type): def get_sample_template(writer): template = [ - ['Account Name', 'Parent Account', 'Account Number', 'Is Group', 'Account Type', 'Root Type'], - ['Application Of Funds(Assets)', '', '', 1, '', 'Asset'], - ['Sources Of Funds(Liabilities)', '', '', 1, '', 'Liability'], - ['Equity', '', '', 1, '', 'Equity'], - ['Expenses', '', '', 1, '', 'Expense'], - ['Income', '', '', 1, '', 'Income'], - ['Bank Accounts', 'Application Of Funds(Assets)', '', 1, 'Bank', 'Asset'], - ['Cash In Hand', 'Application Of Funds(Assets)', '', 1, 'Cash', 'Asset'], - ['Stock Assets', 'Application Of Funds(Assets)', '', 1, 'Stock', 'Asset'], - ['Cost Of Goods Sold', 'Expenses', '', 0, 'Cost of Goods Sold', 'Expense'], - ['Asset Depreciation', 'Expenses', '', 0, 'Depreciation', 'Expense'], - ['Fixed Assets', 'Application Of Funds(Assets)', '', 0, 'Fixed Asset', 'Asset'], - ['Accounts Payable', 'Sources Of Funds(Liabilities)', '', 0, 'Payable', 'Liability'], - ['Accounts Receivable', 'Application Of Funds(Assets)', '', 1, 'Receivable', 'Asset'], - ['Stock Expenses', 'Expenses', '', 0, 'Stock Adjustment', 'Expense'], - ['Sample Bank', 'Bank Accounts', '', 0, 'Bank', 'Asset'], - ['Cash', 'Cash In Hand', '', 0, 'Cash', 'Asset'], - ['Stores', 'Stock Asset', '', 0, 'Stock', 'Asset'], + ["Application Of Funds(Assets)", "", "", 1, "", "Asset"], + ["Sources Of Funds(Liabilities)", "", "", 1, "", "Liability"], + ["Equity", "", "", 1, "", "Equity"], + ["Expenses", "", "", 1, "", "Expense"], + ["Income", "", "", 1, "", "Income"], + ["Bank Accounts", "Application Of Funds(Assets)", "", 1, "Bank", "Asset"], + ["Cash In Hand", "Application Of Funds(Assets)", "", 1, "Cash", "Asset"], + ["Stock Assets", "Application Of Funds(Assets)", "", 1, "Stock", "Asset"], + ["Cost Of Goods Sold", "Expenses", "", 0, "Cost of Goods Sold", "Expense"], + ["Asset Depreciation", "Expenses", "", 0, "Depreciation", "Expense"], + ["Fixed Assets", "Application Of Funds(Assets)", "", 0, "Fixed Asset", "Asset"], + ["Accounts Payable", "Sources Of Funds(Liabilities)", "", 0, "Payable", "Liability"], + ["Accounts Receivable", "Application Of Funds(Assets)", "", 1, "Receivable", "Asset"], + ["Stock Expenses", "Expenses", "", 0, "Stock Adjustment", "Expense"], + ["Sample Bank", "Bank Accounts", "", 0, "Bank", "Asset"], + ["Cash", "Cash In Hand", "", 0, "Cash", "Asset"], + ["Stores", "Stock Assets", "", 0, "Stock", "Asset"], ] for row in template: From e2f53cdc83ef67f1f77311a3b166788d0dbf5e95 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 13 May 2020 16:11:22 +0530 Subject: [PATCH 494/929] fix: error log title for failing bank transactions (cherry picked from commit 4a9fd9ef6d5e34eb6f04deb0423c93f33f0b3028) --- .../doctype/plaid_settings/plaid_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index 970793f7468..c175eaaa251 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -209,7 +209,7 @@ def new_bank_transaction(transaction): result.append(new_transaction.name) except Exception: - frappe.throw(frappe.get_traceback()) + frappe.throw(title=_('Bank transaction creation error')) return result From 3e321d5c24c07d33e47beddbf730c35c98e451c6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 16 May 2020 04:59:43 +0530 Subject: [PATCH 495/929] fix: promotional scheme not able to savce --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 086f42b293f..983f1ef85a0 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -103,7 +103,7 @@ class PricingRule(Document): self.same_item = 1 def validate_max_discount(self): - if self.rate_or_discount == "Discount Percentage" and self.items: + if self.rate_or_discount == "Discount Percentage" and self.get("items"): for d in self.items: max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount") if max_discount and flt(self.discount_percentage) > flt(max_discount): From 1045dc49f58cac1e91262a9d7d3ea8bb0986eec2 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Sun, 17 May 2020 20:17:39 +0530 Subject: [PATCH 496/929] fix: Future date half day validation (#21719) * fix: Future date half day validation * fix: Allow half day attendance only via leave application Co-authored-by: Nabin Hait --- erpnext/hr/doctype/attendance/attendance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index e612c3d7572..f23b4220849 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -38,7 +38,7 @@ class Attendance(Document): date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") # leaves can be marked for future dates - if self.status not in ('On Leave', 'Half Day') and getdate(self.attendance_date) > getdate(nowdate()): + if self.status != 'On Leave' and not self.leave_application and getdate(self.attendance_date) > getdate(nowdate()): frappe.throw(_("Attendance can not be marked for future dates")) elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining): frappe.throw(_("Attendance date can not be less than employee's joining date")) From 7f2aedb67c1a4d01b16490c39096f845df84bbb5 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 17 May 2020 20:28:21 +0530 Subject: [PATCH 497/929] fix: incorrect stock valuation for repack entry (#21737) --- .../stock/doctype/stock_entry/stock_entry.py | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index eb3e3e9af4f..66145c650be 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -360,6 +360,9 @@ class StockEntry(StockController): + self.work_order + ":" + ", ".join(other_ste), DuplicateEntryForWorkOrderError) def set_incoming_rate(self): + if self.purpose == "Repack": + self.set_basic_rate_for_finished_goods() + for d in self.items: if d.s_warehouse: args = self.get_args_for_incoming_rate(d) @@ -472,20 +475,31 @@ class StockEntry(StockController): "allow_zero_valuation": item.allow_zero_valuation_rate, }) - def set_basic_rate_for_finished_goods(self, raw_material_cost, scrap_material_cost): + def set_basic_rate_for_finished_goods(self, raw_material_cost=0, scrap_material_cost=0): + total_fg_qty = 0 + if not raw_material_cost and self.get("items"): + raw_material_cost = sum([flt(row.basic_amount) for row in self.items + if row.s_warehouse and not row.t_warehouse]) + + total_fg_qty = sum([flt(row.qty) for row in self.items + if row.t_warehouse and not row.s_warehouse]) + if self.purpose in ["Manufacture", "Repack"]: for d in self.get("items"): if (d.transfer_qty and (d.bom_no or d.t_warehouse) and (getattr(self, "pro_doc", frappe._dict()).scrap_warehouse != d.t_warehouse)): - if self.work_order \ - and frappe.db.get_single_value("Manufacturing Settings", "material_consumption"): + if (self.work_order and self.purpose == "Manufacture" + and frappe.db.get_single_value("Manufacturing Settings", "material_consumption")): bom_items = self.get_bom_raw_materials(d.transfer_qty) raw_material_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()]) - if raw_material_cost: + if raw_material_cost and self.purpose == "Manufacture": d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate")) d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount")) + elif self.purpose == "Repack" and total_fg_qty: + d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty) + d.basic_amount = d.basic_rate * d.qty def distribute_additional_costs(self): if self.purpose == "Material Issue": From 9b082589551b8462cb4f8fd6d2b670b94cea6666 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Sun, 17 May 2020 17:27:51 +0200 Subject: [PATCH 498/929] fix: remove guest access (#21692) --- erpnext/crm/doctype/lead/lead.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index eb68c679ba5..d0565d19b8d 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -366,7 +366,7 @@ "icon": "fa fa-user", "idx": 5, "image_field": "image", - "modified": "2019-09-19 12:49:02.536647", + "modified": "2020-05-11 20:30:02.536647", "modified_by": "Administrator", "module": "CRM", "name": "Lead", @@ -423,15 +423,6 @@ "read": 1, "report": 1, "role": "Sales User" - }, - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Guest", - "share": 1 } ], "search_fields": "lead_name,lead_owner,status", @@ -439,4 +430,4 @@ "sort_field": "modified", "sort_order": "DESC", "title_field": "lead_name" -} \ No newline at end of file +} From 395da09daeca3b5a9ec8d6ea330d8ec49229cfe7 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 18 May 2020 11:18:56 +0530 Subject: [PATCH 499/929] fix: Patch to set status in old serial no data (#21721) * fix: Patch to set status in old serial no data * fix: Avoid get_doc in patch * fix: fetch all values and check status in one query --- erpnext/patches.txt | 1 + erpnext/patches/v12_0/set_serial_no_status.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 erpnext/patches/v12_0/set_serial_no_status.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 5517cbc9a80..97a45692fd1 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -665,3 +665,4 @@ erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price +erpnext.patches.v12_0.set_serial_no_status diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py new file mode 100644 index 00000000000..4ec84ef0f9e --- /dev/null +++ b/erpnext/patches/v12_0/set_serial_no_status.py @@ -0,0 +1,17 @@ +from __future__ import unicode_literals +import frappe +from frappe.utils import getdate, nowdate + +def execute(): + frappe.reload_doc('stock', 'doctype', 'serial_no') + + for serial_no in frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` + where (status is NULL OR status='')""", as_dict = 1): + if serial_no.get("delivery_document_type"): + status = "Delivered" + elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()): + status = "Expired" + else: + status = "Active" + + frappe.db.set_value("Serial No", serial_no.get("name"), "status", status) \ No newline at end of file From ea4c91f51cecb603ded9f3b7981f093be47d2b53 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 18 May 2020 14:22:42 +0530 Subject: [PATCH 500/929] fix: bom incorrect price list rate for raw material if price list currency is different from company currency (#21586) * fix: bom incorrect price list rate for raw material if price list currency is different from company currency * fixed test cases * fixed base_rate calculation and added plc_conversion_rate trigger --- erpnext/manufacturing/doctype/bom/bom.js | 21 ++++++++- erpnext/manufacturing/doctype/bom/bom.json | 44 ++++++++++++++----- erpnext/manufacturing/doctype/bom/bom.py | 20 +++++++-- erpnext/manufacturing/doctype/bom/test_bom.py | 8 ++-- erpnext/patches.txt | 1 + .../update_price_list_currency_in_bom.py | 31 +++++++++++++ 6 files changed, 105 insertions(+), 20 deletions(-) create mode 100644 erpnext/patches/v12_0/update_price_list_currency_in_bom.py diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 4f08bbc3fc7..cfcab7aea68 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -175,6 +175,12 @@ frappe.ui.form.on("BOM", { }); }, + rm_cost_as_per: function(frm) { + if (in_list(["Valuation Rate", "Last Purchase Rate"], frm.doc.rm_cost_as_per)) { + frm.set_value("plc_conversion_rate", 1.0); + } + }, + routing: function(frm) { if (frm.doc.routing) { frappe.call({ @@ -205,7 +211,7 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ item_code: function(doc, cdt, cdn){ var scrap_items = false; var child = locals[cdt][cdn]; - if(child.doctype == 'BOM Scrap Item') { + if (child.doctype == 'BOM Scrap Item') { scrap_items = true; } @@ -215,8 +221,19 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ get_bom_material_detail(doc, cdt, cdn, scrap_items); }, + + buying_price_list: function(doc) { + this.apply_price_list(); + }, + + plc_conversion_rate: function(doc) { + if (!this.in_apply_price_list) { + this.apply_price_list(); + } + }, + conversion_factor: function(doc, cdt, cdn) { - if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { + if (frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { var item = frappe.get_doc(cdt, cdn); frappe.model.round_floats_in(item, ["qty", "conversion_factor"]); item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json index 63f4f977c59..4ce0ecf3f27 100644 --- a/erpnext/manufacturing/doctype/bom/bom.json +++ b/erpnext/manufacturing/doctype/bom/bom.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "creation": "2013-01-22 15:11:38", "doctype": "DocType", @@ -6,23 +7,25 @@ "engine": "InnoDB", "field_order": [ "item", - "quantity", - "set_rate_of_sub_assembly_item_based_on_bom", + "company", + "item_name", + "uom", "cb0", "is_active", "is_default", "allow_alternative_item", - "image", - "item_name", - "uom", - "currency_detail", - "company", + "set_rate_of_sub_assembly_item_based_on_bom", "project", + "quantity", + "image", + "currency_detail", + "currency", "conversion_rate", "column_break_12", - "currency", "rm_cost_as_per", "buying_price_list", + "price_list_currency", + "plc_conversion_rate", "section_break_21", "with_operations", "column_break_23", @@ -176,7 +179,8 @@ }, { "fieldname": "currency_detail", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Currency and Price List" }, { "fieldname": "company", @@ -324,7 +328,7 @@ }, { "fieldname": "base_scrap_material_cost", - "fieldtype": "Data", + "fieldtype": "Currency", "label": "Scrap Material Cost(Company Currency)", "no_copy": 1, "options": "Company:company:default_currency", @@ -477,13 +481,31 @@ { "fieldname": "column_break_52", "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "depends_on": "eval:doc.rm_cost_as_per=='Price List'", + "fieldname": "plc_conversion_rate", + "fieldtype": "Float", + "label": "Price List Exchange Rate" + }, + { + "allow_on_submit": 1, + "depends_on": "eval:doc.rm_cost_as_per=='Price List'", + "fieldname": "price_list_currency", + "fieldtype": "Link", + "label": "Price List Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1 } ], "icon": "fa fa-sitemap", "idx": 1, "image_field": "image", "is_submittable": 1, - "modified": "2019-11-22 14:35:12.142150", + "links": [], + "modified": "2020-05-05 14:29:32.634952", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM", diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index c898d378c3a..d11077270aa 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -55,11 +55,13 @@ class BOM(WebsiteGenerator): self.validate_main_item() self.validate_currency() self.set_conversion_rate() + self.set_plc_conversion_rate() self.validate_uom_is_interger() self.set_bom_material_details() self.validate_materials() self.validate_operations() self.calculate_cost() + self.update_cost(update_parent=False, from_child_bom=True, save=False) def get_context(self, context): context.parents = [{'name': 'boms', 'title': _('All BOMs') }] @@ -155,7 +157,7 @@ class BOM(WebsiteGenerator): 'rate' : rate, 'qty' : args.get("qty") or args.get("stock_qty") or 1, 'stock_qty' : args.get("qty") or args.get("stock_qty") or 1, - 'base_rate' : rate, + 'base_rate' : flt(rate) * (flt(self.conversion_rate) or 1), 'include_item_in_manufacturing': cint(args['transfer_for_manufacture']) or 0 } @@ -216,7 +218,7 @@ class BOM(WebsiteGenerator): frappe.msgprint(_("{0} not found for item {1}") .format(self.rm_cost_as_per, arg["item_code"]), alert=True) - return flt(rate) / (self.conversion_rate or 1) + return flt(rate) * flt(self.plc_conversion_rate or 1) / (self.conversion_rate or 1) def update_cost(self, update_parent=True, from_child_bom=False, save=True): if self.docstatus == 2: @@ -233,10 +235,15 @@ class BOM(WebsiteGenerator): "stock_uom": d.stock_uom, "conversion_factor": d.conversion_factor }) + if rate: d.rate = rate d.amount = flt(d.rate) * flt(d.qty) - d.db_update() + d.base_rate = flt(d.rate) * flt(self.conversion_rate) + d.base_amount = flt(d.amount) * flt(self.conversion_rate) + + if save: + d.db_update() if self.docstatus == 1: self.flags.ignore_validate_update_after_submit = True @@ -362,6 +369,13 @@ class BOM(WebsiteGenerator): elif self.conversion_rate == 1 or flt(self.conversion_rate) <= 0: self.conversion_rate = get_exchange_rate(self.currency, self.company_currency(), args="for_buying") + def set_plc_conversion_rate(self): + if self.rm_cost_as_per in ["Valuation Rate", "Last Purchase Rate"]: + self.plc_conversion_rate = 1 + elif not self.plc_conversion_rate and self.price_list_currency: + self.plc_conversion_rate = get_exchange_rate(self.price_list_currency, + self.company_currency(), args="for_buying") + def validate_materials(self): """ Validate raw material entries """ diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index 45a7b935d38..3dfd03b1395 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -81,13 +81,13 @@ class TestBOM(unittest.TestCase): # test amounts in selected currency self.assertEqual(bom.operating_cost, 100) - self.assertEqual(bom.raw_material_cost, 8000) - self.assertEqual(bom.total_cost, 8100) + self.assertEqual(bom.raw_material_cost, 351.68) + self.assertEqual(bom.total_cost, 451.68) # test amounts in selected currency self.assertEqual(bom.base_operating_cost, 6000) - self.assertEqual(bom.base_raw_material_cost, 480000) - self.assertEqual(bom.base_total_cost, 486000) + self.assertEqual(bom.base_raw_material_cost, 21100.80) + self.assertEqual(bom.base_total_cost, 27100.80) def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self): frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 97a45692fd1..ea12e2f2210 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -666,3 +666,4 @@ erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_serial_no_status +erpnext.patches.v12_0.update_price_list_currency_in_bom diff --git a/erpnext/patches/v12_0/update_price_list_currency_in_bom.py b/erpnext/patches/v12_0/update_price_list_currency_in_bom.py new file mode 100644 index 00000000000..f5e7b947c23 --- /dev/null +++ b/erpnext/patches/v12_0/update_price_list_currency_in_bom.py @@ -0,0 +1,31 @@ +from __future__ import unicode_literals +import frappe +from frappe.utils import getdate, flt +from erpnext.setup.utils import get_exchange_rate + +def execute(): + frappe.reload_doc("manufacturing", "doctype", "bom") + frappe.reload_doc("manufacturing", "doctype", "bom_item") + + frappe.db.sql(""" UPDATE `tabBOM`, `tabPrice List` + SET + `tabBOM`.price_list_currency = `tabPrice List`.currency, + `tabBOM`.plc_conversion_rate = 1.0 + WHERE + `tabBOM`.buying_price_list = `tabPrice List`.name AND `tabBOM`.docstatus < 2 + AND `tabBOM`.rm_cost_as_per = 'Price List' + """) + + for d in frappe.db.sql(""" + SELECT + bom.creation, bom.name, bom.price_list_currency as currency, + company.default_currency as company_currency + FROM + `tabBOM` as bom, `tabCompany` as company + WHERE + bom.company = company.name AND bom.rm_cost_as_per = 'Price List' AND + bom.price_list_currency != company.default_currency AND bom.docstatus < 2""", as_dict=1): + plc_conversion_rate = get_exchange_rate(d.currency, + d.company_currency, getdate(d.creation), "for_buying") + + frappe.db.set_value("BOM", d.name, "plc_conversion_rate", plc_conversion_rate) \ No newline at end of file From 6dbb02d29347c44b2f29b6d678b251fa019a34d0 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Mon, 18 May 2020 14:26:43 +0530 Subject: [PATCH 501/929] fix: show searchfields result (#21760) --- erpnext/controllers/queries.py | 66 +++++++++++++++----- erpnext/selling/doctype/customer/customer.py | 25 +++++--- 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index c58dd7a8e36..73ed4b01686 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -8,11 +8,14 @@ from frappe.desk.reportview import get_match_cond, get_filters_cond from frappe.utils import nowdate, getdate from collections import defaultdict from erpnext.stock.get_item_details import _get_item_tax_template +from frappe.utils import unique # searches for active employees def employee_query(doctype, txt, searchfield, start, page_len, filters): conditions = [] - return frappe.db.sql("""select name, employee_name from `tabEmployee` + fields = get_fields("Employee", ["name", "employee_name"]) + + return frappe.db.sql("""select {fields} from `tabEmployee` where status = 'Active' and docstatus < 2 and ({key} like %(txt)s @@ -24,6 +27,7 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, employee_name limit %(start)s, %(page_len)s""".format(**{ + 'fields': ", ".join(fields), 'key': searchfield, 'fcond': get_filters_cond(doctype, filters, conditions), 'mcond': get_match_cond(doctype) @@ -34,9 +38,12 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len }) - # searches for leads which are not converted + +# searches for leads which are not converted def lead_query(doctype, txt, searchfield, start, page_len, filters): - return frappe.db.sql("""select name, lead_name, company_name from `tabLead` + fields = get_fields("Lead", ["name", "lead_name", "company_name"]) + + return frappe.db.sql("""select {fields} from `tabLead` where docstatus < 2 and ifnull(status, '') != 'Converted' and ({key} like %(txt)s @@ -50,6 +57,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, lead_name limit %(start)s, %(page_len)s""".format(**{ + 'fields': ", ".join(fields), 'key': searchfield, 'mcond':get_match_cond(doctype) }), { @@ -59,6 +67,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len }) + # searches for customer def customer_query(doctype, txt, searchfield, start, page_len, filters): conditions = [] @@ -69,13 +78,9 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): else: fields = ["name", "customer_name", "customer_group", "territory"] - meta = frappe.get_meta("Customer") - searchfields = meta.get_search_fields() - searchfields = searchfields + [f for f in [searchfield or "name", "customer_name"] \ - if not f in searchfields] - fields = fields + [f for f in searchfields if not f in fields] + fields = get_fields("Customer", fields) - fields = ", ".join(fields) + searchfields = frappe.get_meta("Customer").get_search_fields() searchfields = " or ".join([field + " like %(txt)s" for field in searchfields]) return frappe.db.sql("""select {fields} from `tabCustomer` @@ -88,7 +93,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, customer_name limit %(start)s, %(page_len)s""".format(**{ - "fields": fields, + "fields": ", ".join(fields), "scond": searchfields, "mcond": get_match_cond(doctype), "fcond": get_filters_cond(doctype, filters, conditions).replace('%', '%%'), @@ -99,6 +104,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len }) + # searches for supplier def supplier_query(doctype, txt, searchfield, start, page_len, filters): supp_master_name = frappe.defaults.get_user_default("supp_master_name") @@ -106,7 +112,8 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): fields = ["name", "supplier_group"] else: fields = ["name", "supplier_name", "supplier_group"] - fields = ", ".join(fields) + + fields = get_fields("Supplier", fields) return frappe.db.sql("""select {field} from `tabSupplier` where docstatus < 2 @@ -119,7 +126,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, supplier_name limit %(start)s, %(page_len)s """.format(**{ - 'field': fields, + 'field': ', '.join(fields), 'key': searchfield, 'mcond':get_match_cond(doctype) }), { @@ -129,6 +136,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len }) + def tax_account_query(doctype, txt, searchfield, start, page_len, filters): company_currency = erpnext.get_company_currency(filters.get('company')) @@ -153,6 +161,7 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters): return tax_accounts + def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): conditions = [] @@ -214,10 +223,12 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals "page_len": page_len }, as_dict=as_dict) + def bom(doctype, txt, searchfield, start, page_len, filters): conditions = [] + fields = get_fields("BOM", ["name", "item"]) - return frappe.db.sql("""select tabBOM.name, tabBOM.item + return frappe.db.sql("""select {fields} from tabBOM where tabBOM.docstatus=1 and tabBOM.is_active=1 @@ -227,6 +238,7 @@ 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( + fields=", ".join(fields), fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'), mcond=get_match_cond(doctype).replace('%', '%%'), key=searchfield), @@ -237,13 +249,16 @@ def bom(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len or 20 }) + def get_project_name(doctype, txt, searchfield, start, page_len, filters): cond = '' if filters.get('customer'): cond = """(`tabProject`.customer = %s or ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer"))) - return frappe.db.sql("""select `tabProject`.name from `tabProject` + fields = get_fields("Project", ["name"]) + + return frappe.db.sql("""select {fields} from `tabProject` where `tabProject`.status not in ("Completed", "Cancelled") and {cond} `tabProject`.name like %(txt)s {match_cond} order by @@ -251,6 +266,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): idx desc, `tabProject`.name asc limit {start}, {page_len}""".format( + fields=", ".join(['`tabProject`.{0}'.format(f) for f in fields]), cond=cond, match_cond=get_match_cond(doctype), start=start, @@ -261,8 +277,10 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict): + fields = get_fields("Delivery Note", ["name", "customer", "posting_date"]) + return frappe.db.sql(""" - select `tabDelivery Note`.name, `tabDelivery Note`.customer, `tabDelivery Note`.posting_date + select %(fields)s from `tabDelivery Note` where `tabDelivery Note`.`%(key)s` like %(txt)s and `tabDelivery Note`.docstatus = 1 @@ -277,6 +295,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, ) %(mcond)s order by `tabDelivery Note`.`%(key)s` asc limit %(start)s, %(page_len)s """ % { + "fields": ", ".join(["`tabDelivery Note`.{0}".format(f) for f in fields]), "key": searchfield, "fcond": get_filters_cond(doctype, filters, []), "mcond": get_match_cond(doctype), @@ -342,6 +361,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): order by expiry_date, name desc limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args) + def get_account_list(doctype, txt, searchfield, start, page_len, filters): filter_list = [] @@ -364,6 +384,7 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters): fields = ["name", "parent_account"], limit_start=start, limit_page_length=page_len, as_list=True) + def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select distinct bo.name, bo.blanket_order_type, bo.to_date from `tabBlanket Order` bo, `tabBlanket Order Item` boi @@ -378,6 +399,7 @@ def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters): company = frappe.db.escape(filters.get("company")) )) + @frappe.whitelist() def get_income_account(doctype, txt, searchfield, start, page_len, filters): from erpnext.controllers.queries import get_match_cond @@ -483,6 +505,7 @@ def get_batch_numbers(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(query, filters) + @frappe.whitelist() def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters): item_filters = [ @@ -500,6 +523,7 @@ def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters) ) return item_manufacturers + @frappe.whitelist() def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters): query = """ @@ -513,6 +537,7 @@ def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(query, filters) + @frappe.whitelist() def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters): query = """ @@ -526,6 +551,7 @@ def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(query, filters) + @frappe.whitelist() def get_tax_template(doctype, txt, searchfield, start, page_len, filters): @@ -549,3 +575,13 @@ def get_tax_template(doctype, txt, searchfield, start, page_len, filters): taxes = _get_item_tax_template(args, taxes, for_validate=True) return [(d,) for d in set(taxes)] + + +def get_fields(doctype, fields=[]): + meta = frappe.get_meta(doctype) + fields.extend(meta.get_search_fields()) + + if meta.title_field and not meta.title_field.strip() in fields: + fields.insert(1, meta.title_field.strip()) + + return unique(fields) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index ad5bfa835db..9a5d2ebd910 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -300,11 +300,15 @@ def get_loyalty_programs(doc): return lp_details def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None): + from erpnext.controllers.queries import get_fields + if frappe.db.get_default("cust_master_name") == "Customer Name": fields = ["name", "customer_group", "territory"] else: fields = ["name", "customer_name", "customer_group", "territory"] + fields = get_fields("Customer", fields) + match_conditions = build_match_conditions("Customer") match_conditions = "and {}".format(match_conditions) if match_conditions else "" @@ -312,15 +316,18 @@ def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None): filter_conditions = get_filters_cond(doctype, filters, []) match_conditions += "{}".format(filter_conditions) - return frappe.db.sql("""select %s from `tabCustomer` where docstatus < 2 - and (%s like %s or customer_name like %s) - {match_conditions} - order by - case when name like %s then 0 else 1 end, - case when customer_name like %s then 0 else 1 end, - name, customer_name limit %s, %s""".format(match_conditions=match_conditions) % - (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"), - ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len)) + return frappe.db.sql(""" + select %s + from `tabCustomer` + where docstatus < 2 + and (%s like %s or customer_name like %s) + {match_conditions} + order by + case when name like %s then 0 else 1 end, + case when customer_name like %s then 0 else 1 end, + name, customer_name limit %s, %s + """.format(match_conditions=match_conditions) % (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"), + ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len)) def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, extra_amount=0): From 795d318fcc77f237d86caee69454b70b473fe5bd Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 18 May 2020 14:38:57 +0530 Subject: [PATCH 502/929] fix: Validate Filters in Sales Funnel. (#21761) * fix: Validate Filters in Sales Funnel. * fix: Style fixes (cherry picked from commit c734db5d4505fc08c2a55d1c5be53a10c669e8cb) --- .../selling/page/sales_funnel/sales_funnel.js | 4 ++++ .../selling/page/sales_funnel/sales_funnel.py | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.js b/erpnext/selling/page/sales_funnel/sales_funnel.js index 85c0cd8bf0c..e3d0a55c3a0 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.js +++ b/erpnext/selling/page/sales_funnel/sales_funnel.js @@ -90,6 +90,10 @@ erpnext.SalesFunnel = class SalesFunnel { get_data(btn) { var me = this; + if (!this.company) { + frappe.throw(__("Please Select a Company.")); + } + const method_map = { "sales_funnel": "erpnext.selling.page.sales_funnel.sales_funnel.get_funnel_data", "opp_by_lead_source": "erpnext.selling.page.sales_funnel.sales_funnel.get_opp_by_lead_source", diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.py b/erpnext/selling/page/sales_funnel/sales_funnel.py index d62e2093c69..dba24ef5b00 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.py +++ b/erpnext/selling/page/sales_funnel/sales_funnel.py @@ -8,14 +8,23 @@ from frappe import _ from erpnext.accounts.report.utils import convert import pandas as pd +def validate_filters(from_date, to_date, company): + if from_date and to_date and (from_date >= to_date): + frappe.throw(_("To Date must be greater than From Date")) + + if not company: + frappe.throw(_("Please Select a Company")) + @frappe.whitelist() def get_funnel_data(from_date, to_date, company): + validate_filters(from_date, to_date, company) + active_leads = frappe.db.sql("""select count(*) from `tabLead` where (date(`modified`) between %s and %s) and status != "Do Not Contact" and company=%s""", (from_date, to_date, company))[0][0] active_leads += frappe.db.sql("""select count(distinct contact.name) from `tabContact` contact - left join `tabDynamic Link` dl on (dl.parent=contact.name) where dl.link_doctype='Customer' + left join `tabDynamic Link` dl on (dl.parent=contact.name) where dl.link_doctype='Customer' and (date(contact.modified) between %s and %s) and status != "Passive" """, (from_date, to_date))[0][0] opportunities = frappe.db.sql("""select count(*) from `tabOpportunity` @@ -38,6 +47,8 @@ def get_funnel_data(from_date, to_date, company): @frappe.whitelist() def get_opp_by_lead_source(from_date, to_date, company): + validate_filters(from_date, to_date, company) + opportunities = frappe.get_all("Opportunity", filters=[['status', 'in', ['Open', 'Quotation', 'Replied']], ['company', '=', company], ['transaction_date', 'Between', [from_date, to_date]]], fields=['currency', 'sales_stage', 'opportunity_amount', 'probability', 'source']) if opportunities: @@ -68,11 +79,13 @@ def get_opp_by_lead_source(from_date, to_date, company): @frappe.whitelist() def get_pipeline_data(from_date, to_date, company): + validate_filters(from_date, to_date, company) + opportunities = frappe.get_all("Opportunity", filters=[['status', 'in', ['Open', 'Quotation', 'Replied']], ['company', '=', company], ['transaction_date', 'Between', [from_date, to_date]]], fields=['currency', 'sales_stage', 'opportunity_amount', 'probability']) if opportunities: default_currency = frappe.get_cached_value('Global Defaults', 'None', 'default_currency') - + cp_opportunities = [dict(x, **{'compound_amount': (convert(x['opportunity_amount'], x['currency'], default_currency, to_date) * x['probability']/100)}) for x in opportunities] df = pd.DataFrame(cp_opportunities).groupby(['sales_stage'], as_index=True).agg({'compound_amount': 'sum'}).to_dict() From 632c65cd59b8544a1189bb447418e997da5a1ae0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 18 May 2020 19:54:52 +0530 Subject: [PATCH 503/929] chore: Added change log --- erpnext/change_log/v12/v12_9_0.md | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 erpnext/change_log/v12/v12_9_0.md diff --git a/erpnext/change_log/v12/v12_9_0.md b/erpnext/change_log/v12/v12_9_0.md new file mode 100644 index 00000000000..5d7e0587a34 --- /dev/null +++ b/erpnext/change_log/v12/v12_9_0.md @@ -0,0 +1,43 @@ +## ERPNext v12.9.0 Release Note + +### Enhancements +- Pick List enhancements [#20962](https://github.com/frappe/erpnext/pull/20962) + - The purpose **Delivery Against Sales Order** has been changed to **Delivery** and patch for existing records. + - If the purpose is Delivery then allow rows without Sales Order against them to be mapped to the Delivery Note. + - **Update Current Stock** button to update locations and quantity after Submit. + - Validations if Item Locations table is empty (on creation of Delivery Note, Stock Entry). + - Company-wise fetching of locations and quantity. +- Payment allocation on Payment Entry based on invoice payment terms. [#20945](https://github.com/frappe/erpnext/pull/20945) +- Allow Tax Withholding Category selection at invoice level [#20870](https://github.com/frappe/erpnext/pull/20870) +- Enhanced Employee Leave Balance report, added new fields New Allocation, Expired Leaves. [#21282](https://github.com/frappe/erpnext/pull/21282) +- Provision to set Default Item Manufacturer. [#21197](https://github.com/frappe/erpnext/pull/21197) +- Tax Amount in Credit Note print format should be shown in positive. [#21252](https://github.com/frappe/erpnext/pull/21252) +- On creation of return from employee advance, sets default voucher type as Bank Entry and default debit account as Cash. [#21411](https://github.com/frappe/erpnext/pull/21411) +- On saving a Contact linked with a Lead, update the contact info from Contact into the Lead. [#21469](https://github.com/frappe/erpnext/pull/21469) +- Warning on making payment against paid invoices. [#21501](https://github.com/frappe/erpnext/pull/21501) +- Enhanced Stock Balance report with color-coding. [#21516](https://github.com/frappe/erpnext/pull/21516) +- Added total row in sales analytics report [#21519](https://github.com/frappe/erpnext/pull/21519) +- Asset related accounts must be in company currency. [#21524](https://github.com/frappe/erpnext/pull/21524) +- Accounting Dimensions in Period Closing Voucher. [#21564](https://github.com/frappe/erpnext/pull/21564) +- Renamed LMS to Learning Management System. [#21645](https://github.com/frappe/erpnext/pull/21645) +- Allow half-day attendance only via leave application. [#21719](https://github.com/frappe/erpnext/pull/21719) +- In BOM, allowed Price List in other than company currency. [#21585](https://github.com/frappe/erpnext/pull/21585) + +### Fixes: +- Account Type validation for accounts selected in Asset Category. [#21102](https://github.com/frappe/erpnext/pull/21102) +- Warehouse unset if an item doesn't have a default warehouse. [#21285](https://github.com/frappe/erpnext/pull/21285) +- Bin Requested Qty should be calculated for customer-provided items. [#21300](https://github.com/frappe/erpnext/pull/21300) +- On change of item in sales and purchase transactions, UOM should be reset based on the item's default UOM. [#21254](https://github.com/frappe/erpnext/pull/21254) +- Target warehouse in Delivery Note and Sales Invoice should not be set based on user permission. Patch to fix affected records. [#21359](https://github.com/frappe/erpnext/pull/21359) +- Budget validation against an Accounting Dimension was missing. [#21268](https://github.com/frappe/erpnext/pull/21268) +- On change of qty in Stock Entry, the fields in base currency were not set for non-serialized items. [#21389](https://github.com/frappe/erpnext/pull/21389) +- Payment request not able to make against fees [#21486](https://github.com/frappe/erpnext/pull/21486) +- Cost Center renaming is allowed only from Cost Center form [#21503](https://github.com/frappe/erpnext/pull/21503) +- Accounts Payable report showing the advance amount of other company [#21548](https://github.com/frappe/erpnext/pull/21548) +- Heatmap was not working for customer and supplier [#21578](https://github.com/frappe/erpnext/pull/21578) +- Item tax template set in the Item master is not fetched into the sales invoice and taxes are not shown in the offline pos. [#21714](https://github.com/frappe/erpnext/pull/21714) +- Validate duplicate creation of expiry ledger entry for carry forward allocation and patch to remove duplicate ledgers. [#21505](https://github.com/frappe/erpnext/pull/21505) +- Dimensions were not getting added for some GL Entries and were missing in some recently added transactions. [#21689](https://github.com/frappe/erpnext/pull/21689) +- In Repack entry, set rate for finished goods based on the cost of raw materials. [#21736](https://github.com/frappe/erpnext/pull/21736) +- Removed Guest access from Lead. [#21692](https://github.com/frappe/erpnext/pull/21692) +- Standard and Custom queries did not show search fields for Link fields. [#21685](https://github.com/frappe/erpnext/pull/21685) \ No newline at end of file From 8b144a38cc82484133953026cee72d80684dfff2 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Tue, 19 May 2020 14:27:01 +0530 Subject: [PATCH 504/929] fix: add naming series (#21770) --- .../doctype/quality_meeting/quality_meeting.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json index 0849fd7aeb0..3818f566c17 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json @@ -1,10 +1,11 @@ { - "autoname": "format:MTNG-{date}", + "autoname": "naming_series:", "creation": "2018-10-15 16:25:41.548432", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "naming_series", "date", "cb_00", "status", @@ -53,9 +54,15 @@ "fieldname": "sb_01", "fieldtype": "Section Break", "label": "Minutes" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "MTNG-.YYYY.-.MM.-.DD.-" } ], - "modified": "2019-07-13 19:57:40.500541", + "modified": "2020-05-19 13:18:59.821740", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Meeting", From 113048347cfc3350b01c5c170f7481c8903e8e7f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 19 May 2020 14:36:29 +0530 Subject: [PATCH 505/929] feat: save shipping address and contact to woocommerece customer (bp #21654) (#21771) --- .../connectors/woocommerce_connection.py | 87 +++++++++++++------ erpnext/tests/test_woocommerce.py | 2 +- 2 files changed, 63 insertions(+), 26 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 618865200cf..44f87e0462e 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -49,12 +49,13 @@ def _order(*args, **kwargs): if event == "created": sys_lang = frappe.get_single("System Settings").language or 'en' raw_billing_data = order.get("billing") + raw_shipping_data = order.get("shipping") customer_name = raw_billing_data.get("first_name") + " " + raw_billing_data.get("last_name") - link_customer_and_address(raw_billing_data, customer_name) + link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name) link_items(order.get("line_items"), woocommerce_settings, sys_lang) create_sales_order(order, woocommerce_settings, customer_name, sys_lang) -def link_customer_and_address(raw_billing_data, customer_name): +def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name): customer_woo_com_email = raw_billing_data.get("email") customer_exists = frappe.get_value("Customer", {"woocommerce_email": customer_woo_com_email}) if not customer_exists: @@ -68,38 +69,74 @@ def link_customer_and_address(raw_billing_data, customer_name): customer.customer_name = customer_name customer.woocommerce_email = customer_woo_com_email customer.flags.ignore_mandatory = True - customer.save() + customer.save() if customer_exists: frappe.rename_doc("Customer", old_name, customer_name) - address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email}) + billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"}) + shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"}) + rename_address(billing_address, customer) + rename_address(shipping_address, customer) else: - address = frappe.new_doc("Address") + create_address(raw_billing_data, customer, "Billing") + create_address(raw_shipping_data, customer, "Shipping") + create_contact(raw_billing_data, customer) - address.address_line1 = raw_billing_data.get("address_1", "Not Provided") - address.address_line2 = raw_billing_data.get("address_2", "Not Provided") - address.city = raw_billing_data.get("city", "Not Provided") - address.woocommerce_email = customer_woo_com_email - address.address_type = "Billing" - address.country = frappe.get_value("Country", {"code": raw_billing_data.get("country", "IN").lower()}) - address.state = raw_billing_data.get("state") - address.pincode = raw_billing_data.get("postcode") - address.phone = raw_billing_data.get("phone") - address.email_id = customer_woo_com_email +def create_contact(data, customer): + email = data.get("email", None) + phone = data.get("phone", None) + + if not email and not phone: + return + + contact = frappe.new_doc("Contact") + contact.first_name = data.get("first_name") + contact.last_name = data.get("last_name") + contact.is_primary_contact = 1 + contact.is_billing_contact = 1 + + if phone: + contact.add_phone(phone, is_primary_mobile_no=1, is_primary_phone=1) + + if email: + contact.add_email(email, is_primary=1) + + contact.append("links", { + "link_doctype": "Customer", + "link_name": customer.name + }) + + contact.flags.ignore_mandatory = True + contact.save() + +def create_address(raw_data, customer, address_type): + address = frappe.new_doc("Address") + + address.address_line1 = raw_data.get("address_1", "Not Provided") + address.address_line2 = raw_data.get("address_2", "Not Provided") + address.city = raw_data.get("city", "Not Provided") + address.woocommerce_email = customer.woocommerce_email + address.address_type = address_type + address.country = frappe.get_value("Country", {"code": raw_data.get("country", "IN").lower()}) + address.state = raw_data.get("state") + address.pincode = raw_data.get("postcode") + address.phone = raw_data.get("phone") + address.email_id = customer.woocommerce_email address.append("links", { "link_doctype": "Customer", - "link_name": customer.customer_name + "link_name": customer.name }) + address.flags.ignore_mandatory = True - address = address.save() + address.save() - if customer_exists: - old_address_title = address.name - new_address_title = customer.customer_name + "-billing" - address.address_title = customer.customer_name - address.save() +def rename_address(address, customer): + old_address_title = address.name + new_address_title = customer.name + "-" + address.address_type + address.address_title = customer.customer_name + address.save() - frappe.rename_doc("Address", old_address_title, new_address_title) + frappe.rename_doc("Address", old_address_title, new_address_title) def link_items(items_list, woocommerce_settings, sys_lang): for item_data in items_list: @@ -111,7 +148,7 @@ def link_items(items_list, woocommerce_settings, sys_lang): else: #Create Item item = frappe.new_doc("Item") - + item.item_name = item_data.get("name") item.item_code = _("woocommerce - {0}", sys_lang).format(item_data.get("product_id")) item.woocommerce_id = item_data.get("product_id") @@ -171,7 +208,7 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l add_tax_details(new_sales_order, order.get("shipping_tax"), "Shipping Tax", woocommerce_settings.f_n_f_account) add_tax_details(new_sales_order, order.get("shipping_total"), "Shipping Total", woocommerce_settings.f_n_f_account) - + def add_tax_details(sales_order, price, desc, tax_account_head): sales_order.append("taxes", { "charge_type":"Actual", diff --git a/erpnext/tests/test_woocommerce.py b/erpnext/tests/test_woocommerce.py index ce0f47d685f..df715ab2027 100644 --- a/erpnext/tests/test_woocommerce.py +++ b/erpnext/tests/test_woocommerce.py @@ -24,7 +24,7 @@ class TestWoocommerce(unittest.TestCase): woo_settings.creation_user = "Administrator" woo_settings.save(ignore_permissions=True) - def test_sales_order_for_woocommerece(self): + def test_sales_order_for_woocommerce(self): frappe.flags.woocomm_test_order_data = {"id":75,"parent_id":0,"number":"74","order_key":"wc_order_5aa1281c2dacb","created_via":"checkout","version":"3.3.3","status":"processing","currency":"INR","date_created":"2018-03-08T12:10:04","date_created_gmt":"2018-03-08T12:10:04","date_modified":"2018-03-08T12:10:04","date_modified_gmt":"2018-03-08T12:10:04","discount_total":"0.00","discount_tax":"0.00","shipping_total":"150.00","shipping_tax":"0.00","cart_tax":"0.00","total":"649.00","total_tax":"0.00","prices_include_tax":False,"customer_id":12,"customer_ip_address":"103.54.99.5","customer_user_agent":"mozilla\\/5.0 (x11; linux x86_64) applewebkit\\/537.36 (khtml, like gecko) chrome\\/64.0.3282.186 safari\\/537.36","customer_note":"","billing":{"first_name":"Tony","last_name":"Stark","company":"Woocommerce","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN","email":"tony@gmail.com","phone":"123457890"},"shipping":{"first_name":"Tony","last_name":"Stark","company":"","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN"},"payment_method":"cod","payment_method_title":"Cash on delivery","transaction_id":"","date_paid":"","date_paid_gmt":"","date_completed":"","date_completed_gmt":"","cart_hash":"8e76b020d5790066496f244860c4703f","meta_data":[],"line_items":[{"id":80,"name":"Marvel","product_id":56,"variation_id":0,"quantity":1,"tax_class":"","subtotal":"499.00","subtotal_tax":"0.00","total":"499.00","total_tax":"0.00","taxes":[],"meta_data":[],"sku":"","price":499}],"tax_lines":[],"shipping_lines":[{"id":81,"method_title":"Flat rate","method_id":"flat_rate:1","total":"150.00","total_tax":"0.00","taxes":[],"meta_data":[{"id":623,"key":"Items","value":"Marvel × 1"}]}],"fee_lines":[],"coupon_lines":[],"refunds":[]} order() From a4e92cf577771e309d5000301a1c863b91305c63 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 19 May 2020 15:09:11 +0530 Subject: [PATCH 506/929] refactor: use text editor in issue web form --- erpnext/support/web_form/issues/issues.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/web_form/issues/issues.json b/erpnext/support/web_form/issues/issues.json index 0f15e4737fd..1c0c8ff1584 100644 --- a/erpnext/support/web_form/issues/issues.json +++ b/erpnext/support/web_form/issues/issues.json @@ -18,7 +18,7 @@ "is_standard": 1, "login_required": 1, "max_attachment_size": 0, - "modified": "2020-03-06 05:24:05.749664", + "modified": "2020-05-19 15:08:45.361878", "modified_by": "Administrator", "module": "Support", "name": "issues", @@ -76,7 +76,7 @@ { "allow_read_on_all_link_options": 0, "fieldname": "description", - "fieldtype": "Text", + "fieldtype": "Text Editor", "hidden": 0, "label": "Description", "max_length": 0, From 8209507a8f7625cf2af8326c1ed16263d99c3449 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Tue, 19 May 2020 16:02:41 +0530 Subject: [PATCH 507/929] fix: add naming series (#21775) --- .../doctype/quality_meeting/quality_meeting.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json index 0849fd7aeb0..3818f566c17 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json @@ -1,10 +1,11 @@ { - "autoname": "format:MTNG-{date}", + "autoname": "naming_series:", "creation": "2018-10-15 16:25:41.548432", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "naming_series", "date", "cb_00", "status", @@ -53,9 +54,15 @@ "fieldname": "sb_01", "fieldtype": "Section Break", "label": "Minutes" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "MTNG-.YYYY.-.MM.-.DD.-" } ], - "modified": "2019-07-13 19:57:40.500541", + "modified": "2020-05-19 13:18:59.821740", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Meeting", From 72892a40d6123614ac4fc939fb7ab54058cb34cd Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 19 May 2020 16:36:25 +0530 Subject: [PATCH 508/929] refactor: use text editor in issue web form (#21772) --- erpnext/support/web_form/issues/issues.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/web_form/issues/issues.json b/erpnext/support/web_form/issues/issues.json index 0f15e4737fd..1c0c8ff1584 100644 --- a/erpnext/support/web_form/issues/issues.json +++ b/erpnext/support/web_form/issues/issues.json @@ -18,7 +18,7 @@ "is_standard": 1, "login_required": 1, "max_attachment_size": 0, - "modified": "2020-03-06 05:24:05.749664", + "modified": "2020-05-19 15:08:45.361878", "modified_by": "Administrator", "module": "Support", "name": "issues", @@ -76,7 +76,7 @@ { "allow_read_on_all_link_options": 0, "fieldname": "description", - "fieldtype": "Text", + "fieldtype": "Text Editor", "hidden": 0, "label": "Description", "max_length": 0, From b384ed74cd9f8bcee83e441340948c8fef569e24 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 18:53:08 +0530 Subject: [PATCH 509/929] fix: Instate Invoice not appearing in GSTR-1 report --- erpnext/regional/report/gstr_1/gstr_1.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index a4d0bf59c11..13a9f0bf4ad 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -60,6 +60,9 @@ 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(): + if inv in self.cgst_igst_invoices: + rate = rate/2 + row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) if self.filters.get("type_of_business") == "CDNR": @@ -118,11 +121,11 @@ class Gstr1Report(object): row.append(invoice_details.get(fieldname)) taxable_value = 0 for item_code, net_amount in self.invoice_items.get(invoice).items(): - if item_code in items: - if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []): - taxable_value += abs(net_amount) - elif not self.item_tax_rate.get(invoice): - taxable_value += abs(net_amount) + if item_code in items: + if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []): + taxable_value += abs(net_amount) + elif not self.item_tax_rate.get(invoice): + taxable_value += abs(net_amount) row += [tax_rate or 0, taxable_value] @@ -196,7 +199,7 @@ class Gstr1Report(object): if d.item_code not in self.invoice_items.get(d.parent, {}): self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, sum(i.get('base_net_amount', 0) for i in items - if i.item_code == d.item_code and i.parent == d.parent)) + if i.item_code == d.item_code and i.parent == d.parent)) item_tax_rate = {} @@ -221,6 +224,8 @@ class Gstr1Report(object): self.items_based_on_tax_rate = {} self.invoice_cess = frappe._dict() + self.cgst_igst_invoices = [] + unidentified_gst_accounts = [] for parent, account, item_wise_tax_detail, tax_amount in self.tax_details: if account in self.gst_accounts.cess_account: @@ -243,6 +248,8 @@ class Gstr1Report(object): tax_rate = tax_amounts[0] if cgst_or_sgst: tax_rate *= 2 + if parent not in self.cgst_igst_invoices: + self.cgst_igst_invoices.append(parent) rate_based_dict = self.items_based_on_tax_rate\ .setdefault(parent, {}).setdefault(tax_rate, []) From 07924d3c7cb1567ff737656552c8a7fe6046a7f3 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 May 2020 19:05:17 +0530 Subject: [PATCH 510/929] refactor: changed the fieldtype from data to small text (#21783) --- .../accounts/doctype/sales_invoice/sales_invoice.json | 4 ++-- .../stock/doctype/delivery_note/delivery_note.json | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index ea78c27333e..6e0a30d48e0 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -397,7 +397,7 @@ { "allow_on_submit": 1, "fieldname": "po_no", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Customer's Purchase Order", "no_copy": 1, "print_hide": 1 @@ -1570,7 +1570,7 @@ "idx": 181, "is_submittable": 1, "links": [], - "modified": "2020-04-29 13:37:09.355300", + "modified": "2020-05-19 17:00:57.208696", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 424a7f3c22f..8a2fa397731 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -24,10 +24,10 @@ "return_against", "customer_po_details", "po_no", - "section_break_18", - "pick_list", "column_break_17", "po_date", + "section_break_18", + "pick_list", "contact_info", "shipping_address_name", "shipping_address", @@ -294,7 +294,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "po_no", "fieldname": "customer_po_details", "fieldtype": "Section Break", "label": "Customer PO Details" @@ -302,7 +301,7 @@ { "allow_on_submit": 1, "fieldname": "po_no", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Customer's Purchase Order No", "no_copy": 1, "oldfieldname": "po_no", @@ -316,7 +315,6 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval:doc.po_no", "fieldname": "po_date", "fieldtype": "Date", "label": "Customer's Purchase Order Date", @@ -324,7 +322,6 @@ "oldfieldtype": "Data", "print_hide": 1, "print_width": "100px", - "read_only": 1, "width": "100px" }, { @@ -1240,7 +1237,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-04-17 12:51:41.288600", + "modified": "2020-05-19 17:03:45.880106", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", From e2e5ba345c3647f51f0a14f91ca7b5bd45113db9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 19:56:51 +0530 Subject: [PATCH 511/929] fix: Tax amount in GSTR-1 JSON --- erpnext/regional/report/gstr_1/gstr_1.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 13a9f0bf4ad..fae9dc6e9d8 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -60,9 +60,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(): - if inv in self.cgst_igst_invoices: - rate = rate/2 - row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) if self.filters.get("type_of_business") == "CDNR": @@ -120,9 +117,15 @@ class Gstr1Report(object): else: row.append(invoice_details.get(fieldname)) taxable_value = 0 + + if invoice in self.cgst_igst_invoices: + division_factor = 2 + else: + division_factor = 1 + for item_code, net_amount in self.invoice_items.get(invoice).items(): if item_code in items: - if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []): + if self.item_tax_rate.get(invoice) and tax_rate/division_factor in self.item_tax_rate.get(invoice, {}).get(item_code, []): taxable_value += abs(net_amount) elif not self.item_tax_rate.get(invoice): taxable_value += abs(net_amount) From e85c3a50cded001560f50d8a27718c2355873ecf Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 May 2020 20:30:11 +0530 Subject: [PATCH 512/929] refactor: changed the fieldtype from data to small text (#21789) --- .../accounts/doctype/sales_invoice/sales_invoice.json | 4 ++-- .../stock/doctype/delivery_note/delivery_note.json | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index ea78c27333e..6e0a30d48e0 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -397,7 +397,7 @@ { "allow_on_submit": 1, "fieldname": "po_no", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Customer's Purchase Order", "no_copy": 1, "print_hide": 1 @@ -1570,7 +1570,7 @@ "idx": 181, "is_submittable": 1, "links": [], - "modified": "2020-04-29 13:37:09.355300", + "modified": "2020-05-19 17:00:57.208696", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 424a7f3c22f..8a2fa397731 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -24,10 +24,10 @@ "return_against", "customer_po_details", "po_no", - "section_break_18", - "pick_list", "column_break_17", "po_date", + "section_break_18", + "pick_list", "contact_info", "shipping_address_name", "shipping_address", @@ -294,7 +294,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "po_no", "fieldname": "customer_po_details", "fieldtype": "Section Break", "label": "Customer PO Details" @@ -302,7 +301,7 @@ { "allow_on_submit": 1, "fieldname": "po_no", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Customer's Purchase Order No", "no_copy": 1, "oldfieldname": "po_no", @@ -316,7 +315,6 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval:doc.po_no", "fieldname": "po_date", "fieldtype": "Date", "label": "Customer's Purchase Order Date", @@ -324,7 +322,6 @@ "oldfieldtype": "Data", "print_hide": 1, "print_width": "100px", - "read_only": 1, "width": "100px" }, { @@ -1240,7 +1237,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-04-17 12:51:41.288600", + "modified": "2020-05-19 17:03:45.880106", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", From 05aae4341eb7ef826bd973afc3e1a75ed6daef32 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 20:59:51 +0530 Subject: [PATCH 513/929] fix: Against voucher in General Ledger --- erpnext/accounts/report/general_ledger/general_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index ae407231caa..bb9cfcd886f 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -293,7 +293,7 @@ def get_accountwise_gle(filters, gl_entries, gle_map): data[key].debit_in_account_currency += flt(gle.debit_in_account_currency) data[key].credit_in_account_currency += flt(gle.credit_in_account_currency) - if data[key].against_voucher: + if data[key].against_voucher and gle.against_voucher: data[key].against_voucher += ', ' + gle.against_voucher from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) From 9245133b99e9f6f18a5d7393889aa3374e120278 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Tue, 19 May 2020 18:41:06 +0530 Subject: [PATCH 514/929] tax id is not fetched in when creating sales order from quoation --- erpnext/selling/doctype/sales_order/sales_order.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 6462d3bc8c3..b03b6a6fa7e 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -282,6 +282,7 @@ "width": "100px" }, { + "fetch_from": "customer.tax_id", "fieldname": "tax_id", "fieldtype": "Data", "label": "Tax Id", @@ -1196,7 +1197,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-04-17 12:50:39.640534", + "modified": "2020-05-19 21:36:57.437325", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", From 96a05f65aaaf7cd22e4373b631bc392e1f495c7d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 20:59:51 +0530 Subject: [PATCH 515/929] fix: Against voucher in General Ledger --- erpnext/accounts/report/general_ledger/general_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index ae407231caa..bb9cfcd886f 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -293,7 +293,7 @@ def get_accountwise_gle(filters, gl_entries, gle_map): data[key].debit_in_account_currency += flt(gle.debit_in_account_currency) data[key].credit_in_account_currency += flt(gle.credit_in_account_currency) - if data[key].against_voucher: + if data[key].against_voucher and gle.against_voucher: data[key].against_voucher += ', ' + gle.against_voucher from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) From 0550b3537da3e3762c7dad19f4722d3224156c21 Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 20 May 2020 16:13:34 +0530 Subject: [PATCH 516/929] fix: Validate Payment Gateway only if it exists in Payment Request. (#21807) --- erpnext/accounts/doctype/payment_request/payment_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 4e4eac0610f..c863ce8e7f7 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -69,7 +69,7 @@ class PaymentRequest(Document): elif self.payment_request_type == 'Inward': self.db_set('status', 'Requested') - send_mail = self.payment_gateway_validation() + send_mail = self.payment_gateway_validation() if self.payment_gateway else None ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \ From d53d6cabb463798e6dc0edec431e8d50963cb5e5 Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 20 May 2020 16:13:38 +0530 Subject: [PATCH 517/929] fix: Validate Payment Gateway only if it exists in Payment Request. (#21806) --- erpnext/accounts/doctype/payment_request/payment_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 4e4eac0610f..c863ce8e7f7 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -69,7 +69,7 @@ class PaymentRequest(Document): elif self.payment_request_type == 'Inward': self.db_set('status', 'Requested') - send_mail = self.payment_gateway_validation() + send_mail = self.payment_gateway_validation() if self.payment_gateway else None ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \ From 938cde30e37f71cbfff637d5a6e82a2da30102fa Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 20 May 2020 16:14:58 +0530 Subject: [PATCH 518/929] enable Allow Rename in sales stage (#21803) --- .../crm/doctype/sales_stage/sales_stage.json | 120 +++++------------- 1 file changed, 34 insertions(+), 86 deletions(-) diff --git a/erpnext/crm/doctype/sales_stage/sales_stage.json b/erpnext/crm/doctype/sales_stage/sales_stage.json index 4374bb58314..fe5fce32f86 100644 --- a/erpnext/crm/doctype/sales_stage/sales_stage.json +++ b/erpnext/crm/doctype/sales_stage/sales_stage.json @@ -1,96 +1,44 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:stage_name", - "beta": 0, - "creation": "2018-10-01 09:28:16.399518", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_rename": 1, + "autoname": "field:stage_name", + "creation": "2018-10-01 09:28:16.399518", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "stage_name" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stage_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": "Stage 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, - "translatable": 0, + "fieldname": "stage_name", + "fieldtype": "Data", + "label": "Stage Name", "unique": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-10-01 09:29:43.230378", - "modified_by": "Administrator", - "module": "CRM", - "name": "Sales Stage", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-05-20 14:39:33.300588", + "modified_by": "Administrator", + "module": "CRM", + "name": "Sales Stage", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, "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_views": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From ee56de309172e7b53c1d73efb2c7d1be56cb5de5 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 20 May 2020 16:15:16 +0530 Subject: [PATCH 519/929] enable Allow Rename in sales stage (#21800) --- .../crm/doctype/sales_stage/sales_stage.json | 120 +++++------------- 1 file changed, 34 insertions(+), 86 deletions(-) diff --git a/erpnext/crm/doctype/sales_stage/sales_stage.json b/erpnext/crm/doctype/sales_stage/sales_stage.json index 4374bb58314..85cc9599983 100644 --- a/erpnext/crm/doctype/sales_stage/sales_stage.json +++ b/erpnext/crm/doctype/sales_stage/sales_stage.json @@ -1,96 +1,44 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:stage_name", - "beta": 0, - "creation": "2018-10-01 09:28:16.399518", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_rename": 1, + "autoname": "field:stage_name", + "creation": "2018-10-01 09:28:16.399518", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "stage_name" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stage_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": "Stage 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, - "translatable": 0, + "fieldname": "stage_name", + "fieldtype": "Data", + "label": "Stage Name", "unique": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-10-01 09:29:43.230378", - "modified_by": "Administrator", - "module": "CRM", - "name": "Sales Stage", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-05-20 12:37:17.431879", + "modified_by": "Administrator", + "module": "CRM", + "name": "Sales Stage", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, "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_views": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From c75303b0abf55c212eaee19dcc46a0277889af2c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 20 May 2020 18:24:05 +0530 Subject: [PATCH 520/929] feat: remove website settings from boot (#21801) (#21809) (cherry picked from commit e091789332662a4e27d4c99cc788cb2cf321e7b4) Co-authored-by: Shivam Mishra --- erpnext/startup/boot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index 4ca43a89b8f..2b80fb8dfa1 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -10,7 +10,6 @@ def boot_session(bootinfo): """boot session - send website info if guest""" bootinfo.custom_css = frappe.db.get_value('Style Settings', None, 'custom_css') or '' - bootinfo.website_settings = frappe.get_doc('Website Settings') if frappe.session['user']!='Guest': update_page_info(bootinfo) From 67c9f00917e8eb2a9ea059a9f0d113c24269f52c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 20 May 2020 22:15:12 +0530 Subject: [PATCH 521/929] fix: Project filter in Trial Baalance Report --- erpnext/accounts/report/trial_balance/trial_balance.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 8bd4399e608..5a699b6580a 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -71,7 +71,8 @@ def get_data(filters): opening_balances = get_opening_balances(filters) #add filter inside list so that the query in financial_statements.py doesn't break - filters.project = [filters.project] + if filters.project: + filters.project = [filters.project] set_gl_entries_by_account(filters.company, filters.from_date, filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry)) From d1569b9581eb2168b1081f10bd24d3e21d337935 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 20 May 2020 22:15:12 +0530 Subject: [PATCH 522/929] fix: Project filter in Trial Baalance Report --- erpnext/accounts/report/trial_balance/trial_balance.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 8bd4399e608..5a699b6580a 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -71,7 +71,8 @@ def get_data(filters): opening_balances = get_opening_balances(filters) #add filter inside list so that the query in financial_statements.py doesn't break - filters.project = [filters.project] + if filters.project: + filters.project = [filters.project] set_gl_entries_by_account(filters.company, filters.from_date, filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry)) From 74dd64501cae27a8b41e3935d8d3f89f97469d52 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 21 May 2020 10:09:42 +0530 Subject: [PATCH 523/929] fix: item tax template not applied if valid from is blank --- erpnext/accounts/page/pos/pos.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 06575ac3a69..28c9149c561 100755 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -1459,7 +1459,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.child.serial_no = (this.item_serial_no[this.child.item_code] ? this.item_serial_no[this.child.item_code][0] : ''); - const tax_template_is_valid = frappe.datetime.get_diff(frappe.datetime.now_date(), this.items[0].valid_from) > 0; + const tax_template_is_valid = true; + if (this.items && this.items[0].valid_from) { + tax_template_is_valid = frappe.datetime.get_diff(frappe.datetime.now_date(), + this.items[0].valid_from) > 0; + } this.child.item_tax_template = tax_template_is_valid ? this.items[0].item_tax_template : ''; this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_tax_template]); From 2caac240716e502f9e2dad961ec40ce825572b71 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 21 May 2020 10:09:42 +0530 Subject: [PATCH 524/929] fix: item tax template not applied if valid from is blank --- erpnext/accounts/page/pos/pos.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 06575ac3a69..28c9149c561 100755 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -1459,7 +1459,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.child.serial_no = (this.item_serial_no[this.child.item_code] ? this.item_serial_no[this.child.item_code][0] : ''); - const tax_template_is_valid = frappe.datetime.get_diff(frappe.datetime.now_date(), this.items[0].valid_from) > 0; + const tax_template_is_valid = true; + if (this.items && this.items[0].valid_from) { + tax_template_is_valid = frappe.datetime.get_diff(frappe.datetime.now_date(), + this.items[0].valid_from) > 0; + } this.child.item_tax_template = tax_template_is_valid ? this.items[0].item_tax_template : ''; this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_tax_template]); From ee1c3831bc0f1ecfd39962d0a1de1032b13007ce Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 21 May 2020 12:08:04 +0530 Subject: [PATCH 525/929] fix: plc conversion rate set infinitely (#21821) --- erpnext/manufacturing/doctype/bom/bom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index cfcab7aea68..74169c80d35 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -228,7 +228,7 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ plc_conversion_rate: function(doc) { if (!this.in_apply_price_list) { - this.apply_price_list(); + this.apply_price_list(null, true); } }, From 2894640d56e446625b405ddbebf74766456b0d22 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 21 May 2020 12:08:15 +0530 Subject: [PATCH 526/929] fix: plc conversion rate set infinitely (#21822) --- erpnext/manufacturing/doctype/bom/bom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index cfcab7aea68..74169c80d35 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -228,7 +228,7 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ plc_conversion_rate: function(doc) { if (!this.in_apply_price_list) { - this.apply_price_list(); + this.apply_price_list(null, true); } }, From a8b2f222eaccf09cc599187544c6641443ab51cd Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 May 2020 13:11:48 +0530 Subject: [PATCH 527/929] fix: Fetch customer into Delivery Note from Pick List --- erpnext/stock/doctype/pick_list/pick_list.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 917524929e1..c79025c1fb7 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -300,6 +300,7 @@ def create_delivery_note(source_name, target_doc=None): set_delivery_note_missing_values(delivery_note) delivery_note.pick_list = pick_list.name + delivery_note.customer = pick_list.customer if pick_list.customer else None return delivery_note From 3c84ef3b5e8e61badc8c638815b9890ef342d019 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 May 2020 13:11:48 +0530 Subject: [PATCH 528/929] fix: Fetch customer into Delivery Note from Pick List --- erpnext/stock/doctype/pick_list/pick_list.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 917524929e1..c79025c1fb7 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -300,6 +300,7 @@ def create_delivery_note(source_name, target_doc=None): set_delivery_note_missing_values(delivery_note) delivery_note.pick_list = pick_list.name + delivery_note.customer = pick_list.customer if pick_list.customer else None return delivery_note From d1458c6c0a63ae43fa253842ea6699acbdc65ecf Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 May 2020 14:03:29 +0530 Subject: [PATCH 529/929] fix: Supplier Invoice No not fetched in Import Supplier Invoice --- .../import_supplier_invoice/import_supplier_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py index 72fe17fb379..203ea8c865a 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py @@ -58,7 +58,7 @@ class ImportSupplierInvoice(Document): "naming_series": self.invoice_series, "document_type": line.TipoDocumento.text, "bill_date": get_datetime_str(line.Data.text), - "invoice_no": line.Numero.text, + "bill_no": line.Numero.text, "total_discount": 0, "items": [], "buying_price_list": self.default_buying_price_list @@ -249,7 +249,7 @@ def create_supplier(supplier_group, args): return existing_supplier_name else: - + new_supplier = frappe.new_doc("Supplier") new_supplier.supplier_name = args.supplier new_supplier.supplier_group = supplier_group From cb74ff870db56f7b717ec6db8671ce1afcbc8485 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 May 2020 14:03:29 +0530 Subject: [PATCH 530/929] fix: Supplier Invoice No not fetched in Import Supplier Invoice --- .../import_supplier_invoice/import_supplier_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py index 72fe17fb379..203ea8c865a 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py @@ -58,7 +58,7 @@ class ImportSupplierInvoice(Document): "naming_series": self.invoice_series, "document_type": line.TipoDocumento.text, "bill_date": get_datetime_str(line.Data.text), - "invoice_no": line.Numero.text, + "bill_no": line.Numero.text, "total_discount": 0, "items": [], "buying_price_list": self.default_buying_price_list @@ -249,7 +249,7 @@ def create_supplier(supplier_group, args): return existing_supplier_name else: - + new_supplier = frappe.new_doc("Supplier") new_supplier.supplier_name = args.supplier new_supplier.supplier_group = supplier_group From ab79a9554ba0830da296581e8d8f2f050c416d1c Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Thu, 21 May 2020 15:20:37 +0550 Subject: [PATCH 531/929] bumped to version 12.9.0 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 9b972e7561e..54ffe3f6a14 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.8.0' +__version__ = '12.9.0' def get_default_company(user=None): '''Get default company for user''' From 69a5a7f11e1477c220a5aa00cdbbeb520714593c Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 21 May 2020 15:23:06 +0530 Subject: [PATCH 532/929] fix(patch): Handle single value in patch --- .../patches/v12_0/remove_duplicate_leave_ledger_entries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index 98a2fcf27ea..b7198cc393f 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -40,5 +40,5 @@ def get_duplicate_records(): def delete_duplicate_ledger_entries(duplicate_records_list): """Delete duplicate leave ledger entries.""" - if duplicate_records_list: - frappe.db.sql(''' DELETE FROM `tabLeave Ledger Entry` WHERE name in {0}'''.format(tuple(duplicate_records_list))) #nosec \ No newline at end of file + if not duplicate_records_list: return + frappe.db.sql('''DELETE FROM `tabLeave Ledger Entry` WHERE name in %s''', ((tuple(duplicate_records_list)), )) From 746e8791871a1d9b4ec8c1c51b0e42b25ee8774f Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Thu, 21 May 2020 15:46:42 +0550 Subject: [PATCH 533/929] bumped to version 12.9.1 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 54ffe3f6a14..aecc1240e7c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.9.0' +__version__ = '12.9.1' def get_default_company(user=None): '''Get default company for user''' From da23bb6bf46e63c3cded8832dd647de86623a418 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 21 May 2020 18:46:38 +0530 Subject: [PATCH 534/929] fix(set_serial_no_status): auto commit on many writes (#21843) --- erpnext/patches/v12_0/set_serial_no_status.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py index 4ec84ef0f9e..abba37d48ef 100644 --- a/erpnext/patches/v12_0/set_serial_no_status.py +++ b/erpnext/patches/v12_0/set_serial_no_status.py @@ -5,8 +5,12 @@ from frappe.utils import getdate, nowdate def execute(): frappe.reload_doc('stock', 'doctype', 'serial_no') - for serial_no in frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` - where (status is NULL OR status='')""", as_dict = 1): + serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` + where (status is NULL OR status='')""", as_dict = 1) + if len(serial_no_list) > 20000: + frappe.db.auto_commit_on_many_writes = True + + for serial_no in serial_no_list: if serial_no.get("delivery_document_type"): status = "Delivered" elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()): @@ -14,4 +18,7 @@ def execute(): else: status = "Active" - frappe.db.set_value("Serial No", serial_no.get("name"), "status", status) \ No newline at end of file + frappe.db.set_value("Serial No", serial_no.get("name"), "status", status) + + if frappe.db.auto_commit_on_many_writes: + frappe.db.auto_commit_on_many_writes = False From c5921c605f8b0335e6082a5a36511a85f7955efd Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 21 May 2020 18:46:42 +0530 Subject: [PATCH 535/929] fix(set_serial_no_status): auto commit on many writes (#21841) --- erpnext/patches/v12_0/set_serial_no_status.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py index 4ec84ef0f9e..abba37d48ef 100644 --- a/erpnext/patches/v12_0/set_serial_no_status.py +++ b/erpnext/patches/v12_0/set_serial_no_status.py @@ -5,8 +5,12 @@ from frappe.utils import getdate, nowdate def execute(): frappe.reload_doc('stock', 'doctype', 'serial_no') - for serial_no in frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` - where (status is NULL OR status='')""", as_dict = 1): + serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` + where (status is NULL OR status='')""", as_dict = 1) + if len(serial_no_list) > 20000: + frappe.db.auto_commit_on_many_writes = True + + for serial_no in serial_no_list: if serial_no.get("delivery_document_type"): status = "Delivered" elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()): @@ -14,4 +18,7 @@ def execute(): else: status = "Active" - frappe.db.set_value("Serial No", serial_no.get("name"), "status", status) \ No newline at end of file + frappe.db.set_value("Serial No", serial_no.get("name"), "status", status) + + if frappe.db.auto_commit_on_many_writes: + frappe.db.auto_commit_on_many_writes = False From 4c779300fddea78c1ddb8ac1a1337d933761cb6d Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 21 May 2020 18:47:30 +0530 Subject: [PATCH 536/929] fix: convert goals point to flt (#21840) --- .../hr/doctype/appraisal_template/appraisal_template.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.py b/erpnext/hr/doctype/appraisal_template/appraisal_template.py index e5d3c42e1bd..d0dfad4be31 100644 --- a/erpnext/hr/doctype/appraisal_template/appraisal_template.py +++ b/erpnext/hr/doctype/appraisal_template/appraisal_template.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cint +from frappe.utils import cint, flt from frappe import _ from frappe.model.document import Document @@ -11,11 +11,11 @@ from frappe.model.document import Document class AppraisalTemplate(Document): def validate(self): self.check_total_points() - - def check_total_points(self): + + def check_total_points(self): total_points = 0 for d in self.get("goals"): - total_points += int(d.per_weightage or 0) + total_points += flt(d.per_weightage) if cint(total_points) != 100: frappe.throw(_("Sum of points for all goals should be 100. It is {0}").format(total_points)) From 030abc14a45cc7c6185f67b846fb369f95d6971e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 21 May 2020 18:57:24 +0530 Subject: [PATCH 537/929] fix(patch): Handle single value in patch (#21823) (#21833) (cherry picked from commit 5cef8db4db7b7ddf4d2f789cd10dcab13ce18987) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- .../patches/v12_0/remove_duplicate_leave_ledger_entries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index 98a2fcf27ea..6353304d7aa 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -40,5 +40,5 @@ def get_duplicate_records(): def delete_duplicate_ledger_entries(duplicate_records_list): """Delete duplicate leave ledger entries.""" - if duplicate_records_list: - frappe.db.sql(''' DELETE FROM `tabLeave Ledger Entry` WHERE name in {0}'''.format(tuple(duplicate_records_list))) #nosec \ No newline at end of file + if not duplicate_records_list: return + frappe.db.sql('''DELETE FROM `tabLeave Ledger Entry` WHERE name in %s''', ((tuple(duplicate_records_list)), )) \ No newline at end of file From 576374a8a730e6e77a0abc417bbd6590d6f80685 Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 22 May 2020 10:49:00 +0530 Subject: [PATCH 538/929] fix: Added Inactive serial no status (#21849) --- erpnext/patches.txt | 2 +- erpnext/patches/v12_0/set_serial_no_status.py | 4 +++- erpnext/stock/doctype/serial_no/serial_no.json | 4 ++-- erpnext/stock/doctype/serial_no/serial_no.py | 2 ++ erpnext/stock/doctype/serial_no/serial_no_list.js | 2 ++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ea12e2f2210..78e9bf5e313 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -665,5 +665,5 @@ erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price -erpnext.patches.v12_0.set_serial_no_status +erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py index abba37d48ef..3b5f5ef3407 100644 --- a/erpnext/patches/v12_0/set_serial_no_status.py +++ b/erpnext/patches/v12_0/set_serial_no_status.py @@ -5,7 +5,7 @@ from frappe.utils import getdate, nowdate def execute(): frappe.reload_doc('stock', 'doctype', 'serial_no') - serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` + serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date, warehouse from `tabSerial No` where (status is NULL OR status='')""", as_dict = 1) if len(serial_no_list) > 20000: frappe.db.auto_commit_on_many_writes = True @@ -15,6 +15,8 @@ def execute(): status = "Delivered" elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()): status = "Expired" + elif not serial_no.get("warehouse"): + status = "Inactive" else: status = "Active" diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index 731a7302797..d9f8b627545 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.json +++ b/erpnext/stock/doctype/serial_no/serial_no.json @@ -420,14 +420,14 @@ "fieldtype": "Select", "in_standard_filter": 1, "label": "Status", - "options": "\nActive\nDelivered\nExpired", + "options": "\nActive\nInactive\nDelivered\nExpired", "read_only": 1 } ], "icon": "fa fa-barcode", "idx": 1, "links": [], - "modified": "2020-04-08 13:29:58.517772", + "modified": "2020-05-21 19:29:58.517772", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 6fca584c2f2..2cb43ae2db3 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -42,6 +42,8 @@ class SerialNo(StockController): self.status = "Delivered" elif self.warranty_expiry_date and getdate(self.warranty_expiry_date) <= getdate(nowdate()): self.status = "Expired" + elif not self.warehouse: + self.status = "Inactive" else: self.status = "Active" diff --git a/erpnext/stock/doctype/serial_no/serial_no_list.js b/erpnext/stock/doctype/serial_no/serial_no_list.js index 651f790583d..7526d1d8a5c 100644 --- a/erpnext/stock/doctype/serial_no/serial_no_list.js +++ b/erpnext/stock/doctype/serial_no/serial_no_list.js @@ -5,6 +5,8 @@ frappe.listview_settings['Serial No'] = { return [__("Delivered"), "green", "delivery_document_type,is,set"]; } else if (doc.warranty_expiry_date && frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0) { return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set"]; + } else if (!doc.warehouse) { + return [__("Inactive"), "grey", "warehouse,is,not set"]; } else { return [__("Active"), "green", "delivery_document_type,is,not set"]; } From 5ec0289d98c2f0dc55f0c1d0cb90564936c2d4d2 Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 22 May 2020 10:49:07 +0530 Subject: [PATCH 539/929] fix: Added Inactive serial no status (#21850) --- erpnext/patches.txt | 2 +- erpnext/patches/v12_0/set_serial_no_status.py | 4 +++- erpnext/stock/doctype/serial_no/serial_no.json | 4 ++-- erpnext/stock/doctype/serial_no/serial_no.py | 2 ++ erpnext/stock/doctype/serial_no/serial_no_list.js | 2 ++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ea12e2f2210..78e9bf5e313 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -665,5 +665,5 @@ erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price -erpnext.patches.v12_0.set_serial_no_status +erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py index abba37d48ef..3b5f5ef3407 100644 --- a/erpnext/patches/v12_0/set_serial_no_status.py +++ b/erpnext/patches/v12_0/set_serial_no_status.py @@ -5,7 +5,7 @@ from frappe.utils import getdate, nowdate def execute(): frappe.reload_doc('stock', 'doctype', 'serial_no') - serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` + serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date, warehouse from `tabSerial No` where (status is NULL OR status='')""", as_dict = 1) if len(serial_no_list) > 20000: frappe.db.auto_commit_on_many_writes = True @@ -15,6 +15,8 @@ def execute(): status = "Delivered" elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()): status = "Expired" + elif not serial_no.get("warehouse"): + status = "Inactive" else: status = "Active" diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index 731a7302797..d9f8b627545 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.json +++ b/erpnext/stock/doctype/serial_no/serial_no.json @@ -420,14 +420,14 @@ "fieldtype": "Select", "in_standard_filter": 1, "label": "Status", - "options": "\nActive\nDelivered\nExpired", + "options": "\nActive\nInactive\nDelivered\nExpired", "read_only": 1 } ], "icon": "fa fa-barcode", "idx": 1, "links": [], - "modified": "2020-04-08 13:29:58.517772", + "modified": "2020-05-21 19:29:58.517772", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 6fca584c2f2..2cb43ae2db3 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -42,6 +42,8 @@ class SerialNo(StockController): self.status = "Delivered" elif self.warranty_expiry_date and getdate(self.warranty_expiry_date) <= getdate(nowdate()): self.status = "Expired" + elif not self.warehouse: + self.status = "Inactive" else: self.status = "Active" diff --git a/erpnext/stock/doctype/serial_no/serial_no_list.js b/erpnext/stock/doctype/serial_no/serial_no_list.js index 651f790583d..7526d1d8a5c 100644 --- a/erpnext/stock/doctype/serial_no/serial_no_list.js +++ b/erpnext/stock/doctype/serial_no/serial_no_list.js @@ -5,6 +5,8 @@ frappe.listview_settings['Serial No'] = { return [__("Delivered"), "green", "delivery_document_type,is,set"]; } else if (doc.warranty_expiry_date && frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0) { return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set"]; + } else if (!doc.warehouse) { + return [__("Inactive"), "grey", "warehouse,is,not set"]; } else { return [__("Active"), "green", "delivery_document_type,is,not set"]; } From 7522e598db613438fb63080f3f75193e8b633511 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 22 May 2020 10:59:16 +0530 Subject: [PATCH 540/929] fix: set customer and supplier details using sql (#21846) (#21854) * fix: set customer and supplier details using sql instead of slowing down the query with get_doc and save() we can just use sql to update the required values for customer and supplier Signed-off-by: Chinmay D. Pai * chore: remove extra quote Co-authored-by: Himanshu * fix: update sql query to include tabPrice List Signed-off-by: Chinmay D. Pai Co-authored-by: Himanshu (cherry picked from commit baef43977be81a9db11480cdbce7a3514093e108) Co-authored-by: Chinmay Pai --- ...er_supplier_based_on_type_of_item_price.py | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py index 60aec05466a..b8efb210a03 100644 --- a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py +++ b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py @@ -1,15 +1,29 @@ from __future__ import unicode_literals import frappe + def execute(): - invalid_selling_item_price = frappe.db.sql( - """SELECT name FROM `tabItem Price` WHERE selling = 1 and buying = 0 and (supplier IS NOT NULL or supplier = '')""" - ) - invalid_buying_item_price = frappe.db.sql( - """SELECT name FROM `tabItem Price` WHERE selling = 0 and buying = 1 and (customer IS NOT NULL or customer = '')""" - ) - docs_to_modify = invalid_buying_item_price + invalid_selling_item_price - for d in docs_to_modify: - # saving the doc will auto reset invalid customer/supplier field - doc = frappe.get_doc("Item Price", d[0]) - doc.save() \ No newline at end of file + """ + set proper customer and supplier details for item price + based on selling and buying values + """ + + # update for selling + frappe.db.sql( + """UPDATE `tabItem Price` ip, `tabPrice List` pl + SET ip.`reference` = ip.`customer`, ip.`supplier` = NULL + WHERE ip.`selling` = 1 + AND ip.`buying` = 0 + AND (ip.`supplier` IS NOT NULL OR ip.`supplier` = '') + AND ip.`price_list` = pl.`name` + AND pl.`enabled` = 1""") + + # update for buying + frappe.db.sql( + """UPDATE `tabItem Price` ip, `tabPrice List` pl + SET ip.`reference` = ip.`supplier`, ip.`customer` = NULL + WHERE ip.`selling` = 0 + AND ip.`buying` = 1 + AND (ip.`customer` IS NOT NULL OR ip.`customer` = '') + AND ip.`price_list` = pl.`name` + AND pl.`enabled` = 1""") From b6bbd0efcf4a0784ccbd9c463bfbcd172041e744 Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Fri, 22 May 2020 10:50:13 +0530 Subject: [PATCH 541/929] fix: set customer and supplier details using sql (#21846) * fix: set customer and supplier details using sql instead of slowing down the query with get_doc and save() we can just use sql to update the required values for customer and supplier Signed-off-by: Chinmay D. Pai * chore: remove extra quote Co-authored-by: Himanshu * fix: update sql query to include tabPrice List Signed-off-by: Chinmay D. Pai Co-authored-by: Himanshu (cherry picked from commit baef43977be81a9db11480cdbce7a3514093e108) --- ...er_supplier_based_on_type_of_item_price.py | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py index 60aec05466a..b8efb210a03 100644 --- a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py +++ b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py @@ -1,15 +1,29 @@ from __future__ import unicode_literals import frappe + def execute(): - invalid_selling_item_price = frappe.db.sql( - """SELECT name FROM `tabItem Price` WHERE selling = 1 and buying = 0 and (supplier IS NOT NULL or supplier = '')""" - ) - invalid_buying_item_price = frappe.db.sql( - """SELECT name FROM `tabItem Price` WHERE selling = 0 and buying = 1 and (customer IS NOT NULL or customer = '')""" - ) - docs_to_modify = invalid_buying_item_price + invalid_selling_item_price - for d in docs_to_modify: - # saving the doc will auto reset invalid customer/supplier field - doc = frappe.get_doc("Item Price", d[0]) - doc.save() \ No newline at end of file + """ + set proper customer and supplier details for item price + based on selling and buying values + """ + + # update for selling + frappe.db.sql( + """UPDATE `tabItem Price` ip, `tabPrice List` pl + SET ip.`reference` = ip.`customer`, ip.`supplier` = NULL + WHERE ip.`selling` = 1 + AND ip.`buying` = 0 + AND (ip.`supplier` IS NOT NULL OR ip.`supplier` = '') + AND ip.`price_list` = pl.`name` + AND pl.`enabled` = 1""") + + # update for buying + frappe.db.sql( + """UPDATE `tabItem Price` ip, `tabPrice List` pl + SET ip.`reference` = ip.`supplier`, ip.`customer` = NULL + WHERE ip.`selling` = 0 + AND ip.`buying` = 1 + AND (ip.`customer` IS NOT NULL OR ip.`customer` = '') + AND ip.`price_list` = pl.`name` + AND pl.`enabled` = 1""") From 44d6ce49b57673a6b407822cc2e4eb159c533ef2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 22 May 2020 11:06:19 +0530 Subject: [PATCH 542/929] fix: expense account error message in DN (#21851) (#21856) Changed error message if expense account not set for item in Delivery Note. Earlier: Expense or Difference account is mandatory for Item IT - 6 as it impacts overall stock value After fix: Expense Account not set for Item IT - 6. Please set an Expense Account for the item in the Items table (cherry picked from commit 1763d3e7067c95678089afe17dbb268f4ec9c4df) Co-authored-by: Michelle Alva <50285544+michellealva@users.noreply.github.com> --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index f5c42b49927..d4b0a8d8466 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -224,7 +224,7 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Expense or Difference account is mandatory for Item {0} as it impacts overall stock value").format(item.item_code)) + frappe.throw(_("Expense Account not set for Item {0}. Please set an Expense Account for the item in the Items table").format(item.item_code)) else: is_expense_account = frappe.db.get_value("Account", From d2491e403ba055ce5cd3b637716f372774d9fc67 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 22 May 2020 11:43:14 +0530 Subject: [PATCH 543/929] fix: convert goals point to flt (#21840) (#21858) (cherry picked from commit 4c779300fddea78c1ddb8ac1a1337d933761cb6d) Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> --- .../hr/doctype/appraisal_template/appraisal_template.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.py b/erpnext/hr/doctype/appraisal_template/appraisal_template.py index e5d3c42e1bd..d0dfad4be31 100644 --- a/erpnext/hr/doctype/appraisal_template/appraisal_template.py +++ b/erpnext/hr/doctype/appraisal_template/appraisal_template.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cint +from frappe.utils import cint, flt from frappe import _ from frappe.model.document import Document @@ -11,11 +11,11 @@ from frappe.model.document import Document class AppraisalTemplate(Document): def validate(self): self.check_total_points() - - def check_total_points(self): + + def check_total_points(self): total_points = 0 for d in self.get("goals"): - total_points += int(d.per_weightage or 0) + total_points += flt(d.per_weightage) if cint(total_points) != 100: frappe.throw(_("Sum of points for all goals should be 100. It is {0}").format(total_points)) From 115cedb8e9a31e522a7b7f68826348e0354ca774 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 22 May 2020 12:27:19 +0530 Subject: [PATCH 544/929] fix: Remove duplicate leave ledger entry (#21864) --- .../remove_duplicate_leave_ledger_entries.py | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index 6353304d7aa..438f0f4ad95 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -6,6 +6,7 @@ import frappe def execute(): """Delete duplicate leave ledger entries of type allocation created.""" + frappe.reload_doc('hr', 'doctype', 'leave_ledger_entry') if not frappe.db.a_row_exists("Leave Ledger Entry"): return @@ -15,27 +16,16 @@ def execute(): def get_duplicate_records(): """Fetch all but one duplicate records from the list of expired leave allocation.""" return frappe.db.sql_list(""" - WITH duplicate_records AS - (SELECT - name, transaction_name, is_carry_forward, - ROW_NUMBER() over(partition by transaction_name order by creation)as row - FROM `tabLeave Ledger Entry` l - WHERE (EXISTS - (SELECT name - FROM `tabLeave Ledger Entry` - WHERE - transaction_name = l.transaction_name - AND transaction_type = 'Leave Allocation' - AND name <> l.name - AND employee = l.employee - AND docstatus = 1 - AND leave_type = l.leave_type - AND is_carry_forward=l.is_carry_forward - AND to_date = l.to_date - AND from_date = l.from_date - AND is_expired = 1 - ))) - SELECT name FROM duplicate_records WHERE row > 1 + SELECT name + FROM `tabLeave Ledger Entry` + WHERE + transaction_type = 'Leave Allocation' + AND docstatus = 1 + AND is_expired = 1 + GROUP BY + employee, transaction_name, leave_type, is_carry_forward, from_date, to_date + HAVING + count(name) > 1 """) def delete_duplicate_ledger_entries(duplicate_records_list): From 5f82999397db79367b45efef72d1dceeba63886e Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 22 May 2020 12:29:19 +0530 Subject: [PATCH 545/929] fix: Item tax template not getting mapped from source to traget doc (#21863) --- erpnext/public/js/controllers/transaction.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 4b40c8d4c75..801b6fce4a6 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1614,8 +1614,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if(!r.exc) { $.each(me.frm.doc.items || [], function(i, item) { if(item.item_code && r.message.hasOwnProperty(item.item_code)) { - item.item_tax_template = r.message[item.item_code].item_tax_template; - item.item_tax_rate = r.message[item.item_code].item_tax_rate; + if (!item.item_tax_template) { + item.item_tax_template = r.message[item.item_code].item_tax_template; + item.item_tax_rate = r.message[item.item_code].item_tax_rate; + } me.add_taxes_from_item_tax_template(item.item_tax_rate); } else { item.item_tax_template = ""; From 589c5335d1068c4080029a509f09e69993f520bb Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 22 May 2020 12:41:07 +0530 Subject: [PATCH 546/929] fix: allow on submit for the delivery date field not working for sales order --- .../selling/doctype/sales_order_item/sales_order_item.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index c95f236ebba..722a1adc2fa 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -150,6 +150,7 @@ "width": "300px" }, { + "allow_on_submit": 1, "columns": 2, "depends_on": "eval: !parent.skip_delivery_note", "fieldname": "delivery_date", @@ -757,8 +758,7 @@ ], "idx": 1, "istable": 1, - "links": [], - "modified": "2020-03-05 14:20:28.085117", + "modified": "2020-05-22 12:40:01.696076", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", From 1287f16acd59965822f9aa160908d8b984e2d63b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 22 May 2020 13:13:04 +0530 Subject: [PATCH 547/929] fix: Remove duplicate leave ledger entry (#21867) --- .../remove_duplicate_leave_ledger_entries.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index 438f0f4ad95..24286dcebf9 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -15,8 +15,8 @@ def execute(): def get_duplicate_records(): """Fetch all but one duplicate records from the list of expired leave allocation.""" - return frappe.db.sql_list(""" - SELECT name + return frappe.db.sql(""" + SELECT name, employee, transaction_name, leave_type, is_carry_forward, from_date, to_date FROM `tabLeave Ledger Entry` WHERE transaction_type = 'Leave Allocation' @@ -26,9 +26,21 @@ def get_duplicate_records(): employee, transaction_name, leave_type, is_carry_forward, from_date, to_date HAVING count(name) > 1 + ORDER BY + creation """) def delete_duplicate_ledger_entries(duplicate_records_list): """Delete duplicate leave ledger entries.""" if not duplicate_records_list: return - frappe.db.sql('''DELETE FROM `tabLeave Ledger Entry` WHERE name in %s''', ((tuple(duplicate_records_list)), )) \ No newline at end of file + for d in duplicate_records_list: + frappe.db.sql(''' + DELETE FROM `tabLeave Ledger Entry` + WHERE name != %s + AND employee = %s + AND transaction_name = %s + AND leave_type = %s + AND is_carry_forward = %s + AND from_date = %s + AND to_date = %s + ''', tuple(d)) \ No newline at end of file From 5447decd9e1a4ade5e617bdc2d57925aefa048a0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 22 May 2020 13:13:09 +0530 Subject: [PATCH 548/929] fix: Remove duplicate leave ledger entry (#21870) * fix: Remove duplicate leave ledger entry * fix: Remove duplicate leave ledger entry --- .../remove_duplicate_leave_ledger_entries.py | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index b7198cc393f..6b1b601db19 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -6,6 +6,7 @@ import frappe def execute(): """Delete duplicate leave ledger entries of type allocation created.""" + frappe.reload_doc('hr', 'doctype', 'leave_ledger_entry') if not frappe.db.a_row_exists("Leave Ledger Entry"): return @@ -14,31 +15,32 @@ def execute(): def get_duplicate_records(): """Fetch all but one duplicate records from the list of expired leave allocation.""" - return frappe.db.sql_list(""" - WITH duplicate_records AS - (SELECT - name, transaction_name, is_carry_forward, - ROW_NUMBER() over(partition by transaction_name order by creation)as row - FROM `tabLeave Ledger Entry` l - WHERE (EXISTS - (SELECT name - FROM `tabLeave Ledger Entry` - WHERE - transaction_name = l.transaction_name - AND transaction_type = 'Leave Allocation' - AND name <> l.name - AND employee = l.employee - AND docstatus = 1 - AND leave_type = l.leave_type - AND is_carry_forward=l.is_carry_forward - AND to_date = l.to_date - AND from_date = l.from_date - AND is_expired = 1 - ))) - SELECT name FROM duplicate_records WHERE row > 1 + return frappe.db.sql(""" + SELECT name, employee, transaction_name, leave_type, is_carry_forward, from_date, to_date + FROM `tabLeave Ledger Entry` + WHERE + transaction_type = 'Leave Allocation' + AND docstatus = 1 + AND is_expired = 1 + GROUP BY + employee, transaction_name, leave_type, is_carry_forward, from_date, to_date + HAVING + count(name) > 1 + ORDER BY + creation """) def delete_duplicate_ledger_entries(duplicate_records_list): """Delete duplicate leave ledger entries.""" if not duplicate_records_list: return - frappe.db.sql('''DELETE FROM `tabLeave Ledger Entry` WHERE name in %s''', ((tuple(duplicate_records_list)), )) + for d in duplicate_records_list: + frappe.db.sql(''' + DELETE FROM `tabLeave Ledger Entry` + WHERE name != %s + AND employee = %s + AND transaction_name = %s + AND leave_type = %s + AND is_carry_forward = %s + AND from_date = %s + AND to_date = %s + ''', tuple(d)) From 485f6cd7a2fc16bc4e6a26d92188f06dfc3106c2 Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Fri, 22 May 2020 13:57:25 +0550 Subject: [PATCH 549/929] bumped to version 12.9.2 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index aecc1240e7c..5a2b018675d 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.9.1' +__version__ = '12.9.2' def get_default_company(user=None): '''Get default company for user''' From 439af124f9ed6581aaa25aa889c5d340b035d586 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Fri, 22 May 2020 13:42:55 +0530 Subject: [PATCH 550/929] fix(patch): rerun remove_duplicate_leave_ledger_entries --- erpnext/patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 78e9bf5e313..74f729678d0 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -662,7 +662,7 @@ erpnext.patches.v12_0.set_updated_purpose_in_pick_list erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 -erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries +erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22 execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_serial_no_status #2020-05-21 From 5d8dafc2ec48b2c9ff3815be38f8033861571467 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Fri, 22 May 2020 14:18:08 +0530 Subject: [PATCH 551/929] fix(patch): rerun remove_duplicate_leave_ledger_entries (#21875) --- erpnext/patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 78e9bf5e313..74f729678d0 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -662,7 +662,7 @@ erpnext.patches.v12_0.set_updated_purpose_in_pick_list erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 -erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries +erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22 execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_serial_no_status #2020-05-21 From 1f6c5c295f23a9fe66e446ff60608e6ec8928347 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Fri, 22 May 2020 15:52:46 +0530 Subject: [PATCH 552/929] fix(patch): rerun remove_duplicate_leave_ledger_entries (#21874) --- erpnext/patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 78e9bf5e313..74f729678d0 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -662,7 +662,7 @@ erpnext.patches.v12_0.set_updated_purpose_in_pick_list erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 -erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries +erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22 execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_serial_no_status #2020-05-21 From b4b6b694af15100bebca5879f21b143e1b5bc984 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 8 May 2020 15:33:35 +0530 Subject: [PATCH 553/929] fix: local variable 'lft' referenced before assignment --- erpnext/stock/doctype/warehouse/warehouse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index 6ed6044f329..47a10c68cd3 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -177,7 +177,7 @@ def convert_to_group_or_ledger(): return frappe.get_doc("Warehouse", args.docname).convert_to_group_or_ledger() def get_child_warehouses(warehouse): - lft, rgt = frappe.get_cached_value("Warehouse", warehouse, [lft, rgt]) + lft, rgt = frappe.get_cached_value("Warehouse", warehouse, ["lft", "rgt"]) return frappe.db.sql_list("""select name from `tabWarehouse` where lft >= %s and rgt =< %s""", (lft, rgt)) From 18b3c4802b6108c63d27dab3f8618e13f8973268 Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Mon, 25 May 2020 15:13:09 +0530 Subject: [PATCH 554/929] fix: pound, gram to Ounce converion factor added (#21710) * fix: pound, grams to Ounce converion factor added * fix: patch date updated for UOM Conversion Factor * fix: grams move to gram * fix: duplicate entry of uom converion factor and patch added * fix: typo fix * fix: minor changes * Update patches.txt Co-authored-by: Marica --- erpnext/patches.txt | 1 + .../patches/v12_0/update_uom_conversion_factor.py | 11 +++++++++++ .../setup_wizard/data/uom_conversion_data.json | 14 ++++++++++++++ .../setup_wizard/operations/install_fixtures.py | 15 ++++++++------- 4 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 erpnext/patches/v12_0/update_uom_conversion_factor.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 74f729678d0..40d0e97e304 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -667,3 +667,4 @@ execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom +erpnext.patches.v12_0.update_uom_conversion_factor diff --git a/erpnext/patches/v12_0/update_uom_conversion_factor.py b/erpnext/patches/v12_0/update_uom_conversion_factor.py new file mode 100644 index 00000000000..b5a20aa6fd9 --- /dev/null +++ b/erpnext/patches/v12_0/update_uom_conversion_factor.py @@ -0,0 +1,11 @@ +from __future__ import unicode_literals +import frappe, json + +def execute(): + from erpnext.setup.setup_wizard.operations.install_fixtures import add_uom_data + + frappe.reload_doc("setup", "doctype", "UOM Conversion Factor") + frappe.reload_doc("setup", "doctype", "UOM") + frappe.reload_doc("stock", "doctype", "UOM Category") + + add_uom_data() \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/uom_conversion_data.json b/erpnext/setup/setup_wizard/data/uom_conversion_data.json index 174ecd5903e..27a917d9db8 100644 --- a/erpnext/setup/setup_wizard/data/uom_conversion_data.json +++ b/erpnext/setup/setup_wizard/data/uom_conversion_data.json @@ -1571,5 +1571,19 @@ "to_uom": "Parts Per Million", "abbr": "ppm", "value": "10000" + }, + { + "category": "Mass", + "from_uom": "Pound", + "to_uom": "Ounce", + "abbr": "oz", + "value": "16" + }, + { + "category": "Mass", + "from_uom": "Gram", + "to_uom": "Ounce", + "abbr": "oz", + "value": "0.035274" } ] \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index ebd7b509396..85e070a97f7 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -335,13 +335,14 @@ def add_uom_data(): "category_name": _(d.get("category")) }).insert(ignore_permissions=True) - uom_conversion = frappe.get_doc({ - "doctype": "UOM Conversion Factor", - "category": _(d.get("category")), - "from_uom": _(d.get("from_uom")), - "to_uom": _(d.get("to_uom")), - "value": d.get("value") - }).insert(ignore_permissions=True) + if not frappe.db.exists("UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}): + uom_conversion = frappe.get_doc({ + "doctype": "UOM Conversion Factor", + "category": _(d.get("category")), + "from_uom": _(d.get("from_uom")), + "to_uom": _(d.get("to_uom")), + "value": d.get("value") + }).insert(ignore_permissions=True) def add_market_segments(): records = [ From b9d5c71405f51df5c3e1a3fefff6349d366df062 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 25 May 2020 15:25:42 +0530 Subject: [PATCH 555/929] fix: filter batches based on item and warehouse in Pick List (#21778) Co-authored-by: Marica --- erpnext/stock/doctype/pick_list/pick_list.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index d46b98b461b..3a5ef769805 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -31,10 +31,16 @@ frappe.ui.form.on('Pick List', { }; }); frm.set_query('item_code', 'locations', () => { + return erpnext.queries.item({ "is_stock_item": 1 }); + }); + frm.set_query('batch_no', 'locations', (frm, cdt, cdn) => { + const row = locals[cdt][cdn]; return { + query: 'erpnext.controllers.queries.get_batch_no', filters: { - is_stock_item: 1 - } + item_code: row.item_code, + warehouse: row.warehouse + }, }; }); }, From 410196974e6cf89e769bee96ba033f81ac7bb274 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 25 May 2020 16:38:22 +0530 Subject: [PATCH 556/929] fix: comparison operator --- erpnext/stock/doctype/warehouse/warehouse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index 47a10c68cd3..312488984cd 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -180,7 +180,7 @@ def get_child_warehouses(warehouse): lft, rgt = frappe.get_cached_value("Warehouse", warehouse, ["lft", "rgt"]) return frappe.db.sql_list("""select name from `tabWarehouse` - where lft >= %s and rgt =< %s""", (lft, rgt)) + where lft >= %s and rgt <= %s""", (lft, rgt)) def get_warehouses_based_on_account(account, company=None): warehouses = [] From 4e70ecb97d9932d9e40bb7401d77f1c47026445f Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 25 May 2020 18:26:42 +0530 Subject: [PATCH 557/929] fix: Item Price and Add to Cart not showing on Website (#21905) * fix: Item Price and Add to Cart not showing on Website * fix: Use None as default argument --- erpnext/shopping_cart/cart.py | 19 ++++++++++++------- erpnext/shopping_cart/product_info.py | 8 +++++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 8e644f4144d..a33f4a12242 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -319,7 +319,7 @@ def apply_cart_settings(party=None, quotation=None): def set_price_list_and_rate(quotation, cart_settings): """set price list based on billing territory""" - _set_price_list(quotation, cart_settings) + _set_price_list(cart_settings, quotation) # reset values quotation.price_list_currency = quotation.currency = \ @@ -334,23 +334,28 @@ def set_price_list_and_rate(quotation, cart_settings): # set it in cookies for using in product page frappe.local.cookie_manager.set_cookie("selling_price_list", quotation.selling_price_list) -def _set_price_list(quotation, cart_settings): +def _set_price_list(cart_settings, quotation=None): """Set price list based on customer or shopping cart default""" from erpnext.accounts.party import get_default_price_list # check if customer price list exists selling_price_list = None - if quotation.party_name: - selling_price_list = frappe.db.get_value('Customer', quotation.party_name, 'default_price_list') + if quotation and quotation.get("party_name"): + selling_price_list = frappe.db.get_value('Customer', quotation.get("party_name"), 'default_price_list') # else check for territory based price list if not selling_price_list: selling_price_list = cart_settings.price_list - if not selling_price_list and quotation.party_name: - selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name)) + party_name = quotation.get("party_name") if quotation else get_party().get("name") - quotation.selling_price_list = selling_price_list + if not selling_price_list and party_name: + selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) + + if quotation: + quotation.selling_price_list = selling_price_list + + return selling_price_list def set_taxes(quotation, cart_settings): """set taxes based on billing territory""" diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index 21ee335125b..7c08f5b5b24 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from erpnext.shopping_cart.cart import _get_cart_quotation +from erpnext.shopping_cart.cart import _get_cart_quotation, _set_price_list from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings \ import get_shopping_cart_settings, show_quantity_in_website from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status @@ -21,9 +21,11 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): if not skip_quotation_creation: cart_quotation = _get_cart_quotation() + selling_price_list = cart_quotation.get("selling_price_list") if cart_quotation else _set_price_list(cart_settings, None) + price = get_price( item_code, - cart_quotation.selling_price_list, + selling_price_list, cart_settings.default_customer_group, cart_settings.company ) @@ -42,7 +44,7 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): if product_info["price"]: if frappe.session.user != "Guest": - item = cart_quotation.get({"item_code": item_code}) + item = cart_quotation.get({"item_code": item_code}) if cart_quotation else None if item: product_info["qty"] = item[0].qty From 8ddf30201f7b6690c1a92a860f37499022a707a2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 25 May 2020 18:38:54 +0530 Subject: [PATCH 558/929] fix: Item Price and Add to Cart not showing on Website (#21905) (#21916) * fix: Item Price and Add to Cart not showing on Website * fix: Use None as default argument (cherry picked from commit 4e70ecb97d9932d9e40bb7401d77f1c47026445f) Co-authored-by: Marica --- erpnext/shopping_cart/cart.py | 19 ++++++++++++------- erpnext/shopping_cart/product_info.py | 8 +++++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 8e644f4144d..a33f4a12242 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -319,7 +319,7 @@ def apply_cart_settings(party=None, quotation=None): def set_price_list_and_rate(quotation, cart_settings): """set price list based on billing territory""" - _set_price_list(quotation, cart_settings) + _set_price_list(cart_settings, quotation) # reset values quotation.price_list_currency = quotation.currency = \ @@ -334,23 +334,28 @@ def set_price_list_and_rate(quotation, cart_settings): # set it in cookies for using in product page frappe.local.cookie_manager.set_cookie("selling_price_list", quotation.selling_price_list) -def _set_price_list(quotation, cart_settings): +def _set_price_list(cart_settings, quotation=None): """Set price list based on customer or shopping cart default""" from erpnext.accounts.party import get_default_price_list # check if customer price list exists selling_price_list = None - if quotation.party_name: - selling_price_list = frappe.db.get_value('Customer', quotation.party_name, 'default_price_list') + if quotation and quotation.get("party_name"): + selling_price_list = frappe.db.get_value('Customer', quotation.get("party_name"), 'default_price_list') # else check for territory based price list if not selling_price_list: selling_price_list = cart_settings.price_list - if not selling_price_list and quotation.party_name: - selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name)) + party_name = quotation.get("party_name") if quotation else get_party().get("name") - quotation.selling_price_list = selling_price_list + if not selling_price_list and party_name: + selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) + + if quotation: + quotation.selling_price_list = selling_price_list + + return selling_price_list def set_taxes(quotation, cart_settings): """set taxes based on billing territory""" diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index 21ee335125b..7c08f5b5b24 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from erpnext.shopping_cart.cart import _get_cart_quotation +from erpnext.shopping_cart.cart import _get_cart_quotation, _set_price_list from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings \ import get_shopping_cart_settings, show_quantity_in_website from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status @@ -21,9 +21,11 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): if not skip_quotation_creation: cart_quotation = _get_cart_quotation() + selling_price_list = cart_quotation.get("selling_price_list") if cart_quotation else _set_price_list(cart_settings, None) + price = get_price( item_code, - cart_quotation.selling_price_list, + selling_price_list, cart_settings.default_customer_group, cart_settings.company ) @@ -42,7 +44,7 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): if product_info["price"]: if frappe.session.user != "Guest": - item = cart_quotation.get({"item_code": item_code}) + item = cart_quotation.get({"item_code": item_code}) if cart_quotation else None if item: product_info["qty"] = item[0].qty From bbec37f71300904a1d9b664398a9bceff71298ea Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 25 May 2020 18:39:26 +0530 Subject: [PATCH 559/929] fix: Validation for dates (#21887) --- erpnext/hr/doctype/upload_attendance/upload_attendance.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py index 1707e3578bf..bea1fef8a8d 100644 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py +++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py @@ -20,6 +20,9 @@ def get_template(): args = frappe.local.form_dict + if getdate(args.from_date) > getdate(args.to_date): + frappe.throw(_("To Date should be greater than From Date")) + w = UnicodeWriter() w = add_header(w) From 70439c9cd83237c068799bc22dbaf7a161cc8dae Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Mon, 25 May 2020 19:22:37 +0550 Subject: [PATCH 560/929] bumped to version 12.9.3 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 5a2b018675d..f6fd5caca94 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.9.2' +__version__ = '12.9.3' def get_default_company(user=None): '''Get default company for user''' From 8ae7bb2bc4b3a4292a0b4a837b7704c15664d140 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 25 May 2020 19:20:54 +0530 Subject: [PATCH 561/929] fix: fetch depreciation amount only if depr entry is made (#21894) --- .../asset_depreciations_and_balances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index d7efbad240d..80bccafd28e 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -111,7 +111,7 @@ def get_assets(filters): 0 end), 0) as depreciation_amount_during_the_period from `tabAsset` a, `tabDepreciation Schedule` ds - where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != '' group by a.asset_category union SELECT a.asset_category, From 277f5273a47057f8bd8a45fddae1eb0c275a07bd Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 25 May 2020 21:46:04 +0530 Subject: [PATCH 562/929] chore: Add Import Supplier Invoice to Menu --- erpnext/config/buying.py | 21 +++++++- .../import_supplier_invoice.json | 48 +++++++++++++++++-- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index 1d4054786e6..29589fbcd48 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -1,8 +1,9 @@ from __future__ import unicode_literals +import frappe from frappe import _ def get_data(): - return [ + config = [ { "label": _("Purchasing"), "icon": "fa fa-star", @@ -243,3 +244,21 @@ def get_data(): }, ] + + regional = { + "label": _("Regional"), + "items": [ + { + "type": "doctype", + "name": "Import Supplier Invoice", + "description": _("Import Italian Supplier Invoice."), + "onboard": 1, + } + ] + } + + countries = frappe.get_all("Company", fields="country") + countries = [country["country"] for country in countries] + if "Italy" in countries: + config.append(regional) + return config \ No newline at end of file diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json index 59e955c23f4..6b45841b465 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json @@ -1,5 +1,4 @@ { - "actions": [], "creation": "2019-10-15 12:33:21.845329", "doctype": "DocType", "editable_grid": 1, @@ -92,13 +91,54 @@ "label": "Upload XML Invoices" } ], - "links": [], - "modified": "2019-12-10 16:37:26.793398", + "modified": "2020-05-25 21:32:49.064579", "modified_by": "Administrator", "module": "Regional", "name": "Import Supplier Invoice", "owner": "Administrator", - "permissions": [], + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User" + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor" + }, + { + "permlevel": 1, + "read": 1, + "role": "Accounts Manager", + "write": 1 + } + ], "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 From 7177514b0db8f8860f9169cdb1cb7a1999e14c19 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 26 May 2020 17:59:24 +0530 Subject: [PATCH 563/929] fix: adding dashboard in course and assessment plan (#21889) (#21919) * Adding dashboards * adding dashboard in course and assessment plan (cherry picked from commit 0a147d5c766a69ee98543fd12f1add57c8792e44) Co-authored-by: Anupam Kumar --- .../assessment_plan_dashboard.py | 17 +++++++++++++ .../doctype/course/course_dashboard.py | 25 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py create mode 100644 erpnext/education/doctype/course/course_dashboard.py diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py new file mode 100644 index 00000000000..c36dfb11b54 --- /dev/null +++ b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py @@ -0,0 +1,17 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'assessment_plan', + 'non_standard_fieldnames': { + }, + 'transactions': [ + { + 'label': _('Assessment'), + 'items': ['Assessment Result'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/course/course_dashboard.py b/erpnext/education/doctype/course/course_dashboard.py new file mode 100644 index 00000000000..752af29a9db --- /dev/null +++ b/erpnext/education/doctype/course/course_dashboard.py @@ -0,0 +1,25 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'course', + 'non_standard_fieldnames': { + }, + 'transactions': [ + { + 'label': _('Course'), + 'items': ['Course Enrollment', 'Course Schedule'] + }, + { + 'label': _('Student'), + 'items': ['Student Group'] + }, + { + 'label': _('Assessment'), + 'items': ['Assessment Plan'] + }, + ] + } \ No newline at end of file From a3ec869c58397915882b4ff1ee583406dc7e8f9a Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 26 May 2020 18:00:27 +0530 Subject: [PATCH 564/929] fix: showing Wrong balance on allocation boundary dates (#21908) --- .../employee_leave_balance.py | 10 +++++----- .../employee_leave_balance_summary.py | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index d20018eef34..d5050e73d46 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt +from frappe.utils import flt, add_days from erpnext.hr.doctype.leave_application.leave_application \ import get_leave_balance_on, get_leaves_for_period @@ -26,8 +26,8 @@ def get_columns(leave_types): for leave_type in leave_types: columns.append(_(leave_type) + " " + _("Opening") + ":Float:160") columns.append(_(leave_type) + " " + _("Allocated") + ":Float:160") - columns.append(_(leave_type) + " " + _("Expired") + ":Float:160") columns.append(_(leave_type) + " " + _("Taken") + ":Float:160") + columns.append(_(leave_type) + " " + _("Expired") + ":Float:160") columns.append(_(leave_type) + " " + _("Balance") + ":Float:160") return columns @@ -84,7 +84,7 @@ def calculate_leaves_details(filters, leave_type, employee): # removing expired leaves leaves_taken = leaves_deducted - remove_expired_leave(ledger_entries) - opening = get_leave_balance_on(employee.name, leave_type, filters.from_date) + opening = get_leave_balance_on(employee.name, leave_type, add_days(filters.from_date, -1)) new_allocation , expired_allocation = get_allocated_and_expired_leaves(ledger_entries, filters.from_date, filters.to_date) @@ -94,7 +94,7 @@ def calculate_leaves_details(filters, leave_type, employee): #Formula for calculating closing balance closing = max(opening + new_allocation - (leaves_taken + expired_leaves), 0) - return [opening, new_allocation, expired_leaves, leaves_taken, closing] + return [opening, new_allocation, leaves_taken, expired_leaves, closing] def remove_expired_leave(records): @@ -113,7 +113,7 @@ def get_allocated_and_expired_leaves(records, from_date, to_date): expired_leaves = 0 for record in records: - if record.to_date <= getdate(to_date) and record.leaves>0: + if record.to_date < getdate(to_date) and record.leaves>0: expired_leaves += record.leaves if record.from_date >= getdate(from_date) and record.leaves>0: diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py index 53f8f41915e..21646425bb6 100644 --- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py +++ b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py @@ -45,16 +45,16 @@ def get_columns(): 'fieldtype': 'Float', 'fieldname': 'new_allocation', 'width': 120, - }, { - 'label': _('Expired Leaves'), - 'fieldtype': 'Float', - 'fieldname': 'expired_leaves', - 'width': 120, }, { 'label': _('Leaves Taken'), 'fieldtype': 'float', 'fieldname': 'leaves_taken', 'width': 120, + }, { + 'label': _('Expired Leaves'), + 'fieldtype': 'Float', + 'fieldname': 'expired_leaves', + 'width': 120, }, { 'label': _('Closing Balance'), 'fieldtype': 'float', @@ -96,8 +96,8 @@ def get_data(filters): leave_details = calculate_leaves_details(filters, leave_type, employee) row.opening_balance = flt(leave_details[0]) row.new_allocation = flt(leave_details[1]) - row.expired_leaves = flt(leave_details[2]) - row.leaves_taken = flt(leave_details[3]) + row.leaves_taken = flt(leave_details[2]) + row.expired_leaves = flt(leave_details[3]) row.closing_balance = flt(leave_details[4]) data.append(row) From b09417873405bbbab0aec1173a487f5383c24658 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 26 May 2020 18:02:08 +0530 Subject: [PATCH 565/929] fix(UAE): Incorrect VAT rate display in Sales Invoice (#21883) --- erpnext/regional/united_arab_emirates/utils.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index a01c6ceec36..772bbf5914b 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -1,6 +1,8 @@ from __future__ import unicode_literals +import frappe from frappe.utils import flt from erpnext.controllers.taxes_and_totals import get_itemised_tax +from six import iteritems def update_itemised_tax_data(doc): if not doc.taxes: return @@ -9,7 +11,14 @@ def update_itemised_tax_data(doc): for row in doc.items: tax_rate = 0.0 - if itemised_tax.get(row.item_code): + item_tax_rate = frappe.parse_json(row.item_tax_rate) + + # First check if tax rate is present + # If not then look up in item_wise_tax_detail + if item_tax_rate: + for account, rate in iteritems(item_tax_rate): + tax_rate += rate + elif itemised_tax.get(row.item_code): tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()]) row.tax_rate = flt(tax_rate, row.precision("tax_rate")) From 9ad46f7487d56f96d9088acf98e189935583723b Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 26 May 2020 18:08:08 +0530 Subject: [PATCH 566/929] fix: Added column Expired Leave (#21859) --- .../doctype/leave_application/leave_application.py | 11 ++++++++++- .../leave_application_dashboard.html | 12 +++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 6ef4a303d87..1622fb38eec 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -428,14 +428,23 @@ def get_leave_details(employee, date): leave_allocation = {} for d in allocation_records: allocation = allocation_records.get(d, frappe._dict()) + + total_allocated_leaves = frappe.db.get_value('Leave Allocation', { + 'from_date': ('<=', date), + 'to_date': ('>=', date), + 'leave_type': allocation.leave_type, + }, 'SUM(total_leaves_allocated)') or 0 + remaining_leaves = get_leave_balance_on(employee, d, date, to_date = allocation.to_date, consider_all_leaves_in_the_allocation_period=True) + end_date = allocation.to_date leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, end_date) * -1 leaves_pending = get_pending_leaves_for_period(employee, d, allocation.from_date, end_date) leave_allocation[d] = { - "total_leaves": allocation.total_leaves_allocated, + "total_leaves": total_allocated_leaves, + "expired_leaves": total_allocated_leaves - (remaining_leaves + leaves_taken), "leaves_taken": leaves_taken, "pending_leaves": leaves_pending, "remaining_leaves": remaining_leaves} diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html index 2385b6ac1c4..295f3b43419 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html +++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html @@ -4,11 +4,12 @@
    -

    +

    ${__("Projected Quantity Formula")} diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 0be9f1a8eef..af844816e8b 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -1,1391 +1,321 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2017-10-29 11:53:09.523362", - "custom": 0, - "default_print_format": "", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, - "engine": "InnoDB", + "actions": [], + "autoname": "naming_series:", + "creation": "2017-10-29 11:53:09.523362", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "naming_series", + "company", + "get_items_from", + "column_break1", + "posting_date", + "filters", + "item_code", + "customer", + "warehouse", + "project", + "column_break2", + "from_date", + "to_date", + "sales_orders_detail", + "get_sales_orders", + "sales_orders", + "material_request_detail", + "get_material_request", + "material_requests", + "select_items_to_manufacture_section", + "get_items", + "po_items", + "material_request_planning", + "include_non_stock_items", + "include_subcontracted_items", + "ignore_existing_ordered_qty", + "column_break_25", + "for_warehouse", + "download_materials_required", + "get_items_for_mr", + "section_break_27", + "mr_items", + "other_details", + "total_planned_qty", + "total_produced_qty", + "column_break_32", + "status", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_if_empty": 0, - "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": "Naming Series", - "length": 0, - "no_copy": 0, - "options": "MFG-PP-.YYYY.-", - "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 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "MFG-PP-.YYYY.-", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "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 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_if_empty": 0, - "fieldname": "get_items_from", - "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": "Get Items From", - "length": 0, - "no_copy": 0, - "options": "\nSales Order\nMaterial Request", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "get_items_from", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Get Items From", + "options": "\nSales Order\nMaterial Request" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_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, - "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_break1", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fetch_if_empty": 0, - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Posting Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval: doc.__islocal", - "columns": 0, - "depends_on": "eval: doc.get_items_from", - "description": "", - "fetch_if_empty": 0, - "fieldname": "filters", - "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": "Filters", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "eval: doc.__islocal", + "depends_on": "eval: doc.get_items_from", + "fieldname": "filters", + "fieldtype": "Section Break", + "label": "Filters" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.get_items_from == \"Sales Order\"", - "fetch_if_empty": 0, - "fieldname": "customer", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Customer", - "length": 0, - "no_copy": 0, - "options": "Customer", - "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 - }, + "depends_on": "eval: doc.get_items_from == \"Sales Order\"", + "fieldname": "customer", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Customer", + "options": "Customer" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.get_items_from == \"Material Request\"", - "fetch_if_empty": 0, - "fieldname": "warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval: doc.get_items_from == \"Material Request\"", + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.get_items_from == \"Sales Order\"", - "fetch_if_empty": 0, - "fieldname": "project", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Project", - "length": 0, - "no_copy": 0, - "options": "Project", - "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 - }, + "depends_on": "eval: doc.get_items_from == \"Sales Order\"", + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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_break2", + "fieldtype": "Column Break", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "from_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": "From 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 - }, + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "to_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": "To 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 - }, + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval: doc.__islocal", - "columns": 0, - "depends_on": "eval: doc.get_items_from == \"Sales Order\"", - "fetch_if_empty": 0, - "fieldname": "sales_orders_detail", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Orders Detail", - "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 - }, + "collapsible": 1, + "collapsible_depends_on": "eval: doc.__islocal", + "depends_on": "eval: doc.get_items_from == \"Sales Order\"", + "fieldname": "sales_orders_detail", + "fieldtype": "Section Break", + "label": "Sales Orders" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, - "fieldname": "get_sales_orders", - "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": "Get Sales Orders", - "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 - }, + "fieldname": "get_sales_orders", + "fieldtype": "Button", + "label": "Get Sales Orders" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "sales_orders", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Orders", - "length": 0, - "no_copy": 1, - "options": "Production Plan 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 - }, + "fieldname": "sales_orders", + "fieldtype": "Table", + "label": "Sales Orders", + "no_copy": 1, + "options": "Production Plan Sales Order" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval: doc.__islocal", - "columns": 0, - "depends_on": "eval: doc.get_items_from == \"Material Request\"", - "fetch_if_empty": 0, - "fieldname": "material_request_detail", - "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": "Material Request Detail", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "eval: doc.__islocal", + "depends_on": "eval: doc.get_items_from == \"Material Request\"", + "fieldname": "material_request_detail", + "fieldtype": "Section Break", + "label": "Material Request Detail" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, - "fieldname": "get_material_request", - "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": "Get Material Request", - "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 - }, + "fieldname": "get_material_request", + "fieldtype": "Button", + "label": "Get Material Request" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "material_requests", - "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": "Material Requests", - "length": 0, - "no_copy": 1, - "options": "Production Plan Material Request", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "material_requests", + "fieldtype": "Table", + "label": "Material Requests", + "no_copy": 1, + "options": "Production Plan Material Request" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, - "fieldname": "select_items_to_manufacture_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": "Select Items to Manufacture", - "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": "select_items_to_manufacture_section", + "fieldtype": "Section Break", + "label": "Select Items to Manufacture" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "get_items_from", - "fetch_if_empty": 0, - "fieldname": "get_items", - "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": "Get Items For Work Order", - "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 - }, + "depends_on": "get_items_from", + "fieldname": "get_items", + "fieldtype": "Button", + "label": "Get Items For Work Order" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "po_items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 1, - "options": "Production Plan Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "po_items", + "fieldtype": "Table", + "no_copy": 1, + "options": "Production Plan Item", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "material_request_planning", - "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": "Material Request Planning", - "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": "material_request_planning", + "fieldtype": "Section Break", + "label": "Material Request Planning" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fetch_if_empty": 0, - "fieldname": "include_non_stock_items", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Include Non Stock Items", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "1", + "fieldname": "include_non_stock_items", + "fieldtype": "Check", + "label": "Include Non Stock Items" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fetch_if_empty": 0, - "fieldname": "include_subcontracted_items", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Include Subcontracted Items", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "1", + "fieldname": "include_subcontracted_items", + "fieldtype": "Check", + "label": "Include Subcontracted Items" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If enabled, then system will create the material even if the raw materials are available", - "fetch_if_empty": 0, - "fieldname": "ignore_existing_ordered_qty", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Ignore Existing Projected Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "description": "To know more about projected quantity, click here.", + "fieldname": "ignore_existing_ordered_qty", + "fieldtype": "Check", + "label": "Ignore Existing Projected Quantity" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_25", - "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_25", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "for_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "For Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "for_warehouse", + "fieldtype": "Link", + "label": "For Warehouse", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "download_materials_required", - "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": "Download Materials Required", - "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": "download_materials_required", + "fieldtype": "Button", + "label": "Download Required Materials" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "get_items_for_mr", - "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": "Get Raw Materials For Production", - "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": "get_items_for_mr", + "fieldtype": "Button", + "label": "Get Raw Materials For Production" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "section_break_27", - "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_27", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "mr_items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Material Request Plan Item", - "length": 0, - "no_copy": 1, - "options": "Material Request Plan Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "mr_items", + "fieldtype": "Table", + "label": "Material Request Plan Item", + "no_copy": 1, + "options": "Material Request Plan Item" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_if_empty": 0, - "fieldname": "projected_qty_formula", - "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": "Projected Qty Formula", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "other_details", + "fieldtype": "Section Break", + "label": "Other Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "other_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": "Other Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "total_planned_qty", + "fieldtype": "Float", + "label": "Total Planned Qty", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "total_planned_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Planned Qty", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "total_produced_qty", + "fieldtype": "Float", + "label": "Total Produced Qty", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "total_produced_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Produced Qty", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_32", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_32", - "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 - }, + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "no_copy": 1, + "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled\nMaterial Requested", + "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, - "default": "Draft", - "fetch_if_empty": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 1, - "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled\nMaterial Requested", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Production Plan", - "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", + "label": "Amended From", + "no_copy": 1, + "options": "Production Plan", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_toolbar": 0, - "icon": "fa fa-calendar", - "idx": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-09 12:05:14.300886", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Production Plan", - "name_case": "", - "owner": "Administrator", + ], + "icon": "fa fa-calendar", + "is_submittable": 1, + "links": [], + "modified": "2019-12-04 15:58:50.940460", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Production Plan", + "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": 0, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "Manufacturing User", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "ASC", - "title_field": "", - "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/manufacturing/doctype/production_plan/production_plan_dashboard.py b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py index 91c28555d61..09ec24a67a2 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py @@ -6,7 +6,7 @@ def get_data(): 'fieldname': 'production_plan', 'transactions': [ { - 'label': _('Related'), + 'label': _('Transactions'), 'items': ['Work Order', 'Material Request'] }, ] diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 7e1ea3eb1d9..1989c10c394 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -6,7 +6,7 @@ frappe.ui.form.on("Work Order", { frm.custom_make_buttons = { 'Stock Entry': 'Start', 'Pick List': 'Create Pick List', - 'Job Card': 'Create Job Card', + 'Job Card': 'Create Job Card' }; // Set query for warehouses @@ -132,7 +132,8 @@ frappe.ui.form.on("Work Order", { } if (frm.doc.docstatus===1) { - frm.trigger('show_progress'); + frm.trigger('show_progress_for_items'); + frm.trigger('show_progress_for_operations'); } if (frm.doc.docstatus === 1 @@ -180,89 +181,72 @@ frappe.ui.form.on("Work Order", { make_job_card: function(frm) { let qty = 0; - const fields = [{ - fieldtype: "Link", - fieldname: "operation", - options: "Operation", - label: __("Operation"), - get_query: () => { - const filter_workstation = frm.doc.operations.filter(d => { - if (d.status != "Completed") { - return d; - } - }); + let operations_data = []; - return { - filters: { - name: ["in", (filter_workstation || []).map(d => d.operation)] - } - }; - }, - reqd: true - }, { - fieldtype: "Link", - fieldname: "workstation", - options: "Workstation", - label: __("Workstation"), - get_query: () => { - const operation = dialog.get_value("operation"); - const filter_workstation = frm.doc.operations.filter(d => { - if (d.operation == operation) { - return d; - } - }); - - return { - filters: { - name: ["in", (filter_workstation || []).map(d => d.workstation)] - } - }; - }, - onchange: () => { - const operation = dialog.get_value("operation"); - const workstation = dialog.get_value("workstation"); - if (operation && workstation) { - const row = frm.doc.operations.filter(d => d.operation == operation && d.workstation == workstation)[0]; - qty = frm.doc.qty - row.completed_qty; - - if (qty > 0) { - dialog.set_value("qty", qty); - } - } - }, - reqd: true - }, { - fieldtype: "Float", - fieldname: "qty", - label: __("For Quantity"), - reqd: true - }]; - - const dialog = frappe.prompt(fields, function(data) { - if (data.qty > qty) { - frappe.throw(__("For Quantity must be less than quantity {0}", [qty])); + const dialog = frappe.prompt({fieldname: 'operations', fieldtype: 'Table', label: __('Operations'), + fields: [ + { + fieldtype:'Link', + fieldname:'operation', + label: __('Operation'), + read_only:1, + in_list_view:1 + }, + { + fieldtype:'Link', + fieldname:'workstation', + label: __('Workstation'), + read_only:1, + in_list_view:1 + }, + { + fieldtype:'Data', + fieldname:'name', + label: __('Operation Id') + }, + { + fieldtype:'Float', + fieldname:'pending_qty', + label: __('Pending Qty'), + }, + { + fieldtype:'Float', + fieldname:'qty', + label: __('Quantity to Manufacture'), + read_only:0, + in_list_view:1, + }, + ], + data: operations_data, + in_place_edit: true, + get_data: function() { + return operations_data; } - - if (data.qty <= 0) { - frappe.throw(__("For Quantity must be greater than zero")); - } - + }, function(data) { frappe.call({ method: "erpnext.manufacturing.doctype.work_order.work_order.make_job_card", args: { work_order: frm.doc.name, - operation: data.operation, - workstation: data.workstation, - qty: data.qty - }, - callback: function(r){ - if (r.message) { - var doc = frappe.model.sync(r.message)[0]; - frappe.set_route("Form", doc.doctype, doc.name); - } + operations: data.operations, } }); - }, __("For Job Card")); + }, __("Job Card"), __("Create")); + + var pending_qty = 0; + frm.doc.operations.forEach(data => { + if(data.completed_qty != frm.doc.qty) { + pending_qty = frm.doc.qty - flt(data.completed_qty); + + dialog.fields_dict.operations.df.data.push({ + 'name': data.name, + 'operation': data.operation, + 'workstation': data.workstation, + 'qty': pending_qty, + 'pending_qty': pending_qty, + }); + } + }); + dialog.fields_dict.operations.grid.refresh(); }, make_bom: function(frm) { @@ -278,7 +262,7 @@ frappe.ui.form.on("Work Order", { }); }, - show_progress: function(frm) { + show_progress_for_items: function(frm) { var bars = []; var message = ''; var added_min = false; @@ -312,6 +296,44 @@ frappe.ui.form.on("Work Order", { frm.dashboard.add_progress(__('Status'), bars, message); }, + show_progress_for_operations: function(frm) { + if (frm.doc.operations && frm.doc.operations.length) { + + let progress_class = { + "Work in Progress": "progress-bar-warning", + "Completed": "progress-bar-success" + }; + + let bars = []; + let message = ''; + let title = ''; + let status_wise_oprtation_data = {}; + let total_completed_qty = frm.doc.qty * frm.doc.operations.length; + + frm.doc.operations.forEach(d => { + if (!status_wise_oprtation_data[d.status]) { + status_wise_oprtation_data[d.status] = [d.completed_qty, d.operation]; + } else { + status_wise_oprtation_data[d.status][0] += d.completed_qty; + status_wise_oprtation_data[d.status][1] += ', ' + d.operation; + } + }); + + for (let key in status_wise_oprtation_data) { + title = __("{0} Operations: {1}", [key, status_wise_oprtation_data[key][1].bold()]); + bars.push({ + 'title': title, + 'width': status_wise_oprtation_data[key][0] / total_completed_qty * 100 + '%', + 'progress_class': progress_class[key] + }); + + message += title + '. '; + } + + frm.dashboard.add_progress(__('Status'), bars, message); + } + }, + production_item: function(frm) { if (frm.doc.production_item) { frappe.call({ diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index 0d073a2312b..6152fbb8402 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-01-10 16:34:16", @@ -468,7 +469,8 @@ "idx": 1, "image_field": "image", "is_submittable": 1, - "modified": "2019-08-28 12:29:35.315239", + "links": [], + "modified": "2019-12-04 11:20:04.695123", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 28bcc2e0f9c..83579414812 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -6,7 +6,7 @@ import frappe import json import math from frappe import _ -from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate +from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate, get_link_to_form from frappe.model.document import Document from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict from dateutil.relativedelta import relativedelta @@ -711,21 +711,41 @@ def query_sales_order(production_item): return out @frappe.whitelist() -def make_job_card(work_order, operation, workstation, qty=0): +def make_job_card(work_order, operations): + if isinstance(operations, string_types): + operations = json.loads(operations) + work_order = frappe.get_doc('Work Order', work_order) - row = get_work_order_operation_data(work_order, operation, workstation) - if row: - return create_job_card(work_order, row, qty) + for row in operations: + validate_operation_data(row) + create_job_card(work_order, row, row.get("qty"), auto_create=True) + +def validate_operation_data(row): + if row.get("qty") <= 0: + frappe.throw(_("Quantity to Manufacture can not be zero for the operation {0}") + .format( + frappe.bold(row.get("operation")) + ) + ) + + if row.get("qty") > row.get("pending_qty"): + frappe.throw(_("For operation {0}: Quantity ({1}) can not be greter than pending quantity({2})") + .format( + frappe.bold(row.get("operation")), + frappe.bold(row.get("qty")), + frappe.bold(row.get("pending_qty")) + ) + ) def create_job_card(work_order, row, qty=0, auto_create=False): doc = frappe.new_doc("Job Card") doc.update({ 'work_order': work_order.name, - 'operation': row.operation, - 'workstation': row.workstation, + 'operation': row.get("operation"), + 'workstation': row.get("workstation"), 'posting_date': nowdate(), 'for_quantity': qty or work_order.get('qty', 0), - 'operation_id': row.name, + 'operation_id': row.get("name"), 'bom_no': work_order.bom_no, 'project': work_order.project, 'company': work_order.company, @@ -738,7 +758,7 @@ def create_job_card(work_order, row, qty=0, auto_create=False): if auto_create: doc.flags.ignore_mandatory = True doc.insert() - frappe.msgprint(_("Job card {0} created").format(doc.name)) + frappe.msgprint(_("Job card {0} created").format(get_link_to_form("Job Card", doc.name))) return doc diff --git a/erpnext/manufacturing/doctype/work_order/work_order_calendar.js b/erpnext/manufacturing/doctype/work_order/work_order_calendar.js index c44b1e2700d..7ce05e9b881 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_calendar.js +++ b/erpnext/manufacturing/doctype/work_order/work_order_calendar.js @@ -2,11 +2,13 @@ // For license information, please see license.txt frappe.views.calendar["Work Order"] = { + fields: ["planned_start_date", "planned_end_date", "status", "produced_qty", "qty", "name", "name"], field_map: { "start": "planned_start_date", "end": "planned_end_date", "id": "name", "title": "name", + "status": "status", "allDay": "allDay", "progress": function(data) { return flt(data.produced_qty) / data.qty * 100; diff --git a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py index 0d3c30ea6eb..87c090f99c3 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py +++ b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py @@ -6,7 +6,8 @@ def get_data(): 'fieldname': 'work_order', 'transactions': [ { - 'items': ['Pick List', 'Stock Entry', 'Job Card'] + 'label': _('Transactions'), + 'items': ['Stock Entry', 'Job Card', 'Pick List'] } ] } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json index 75d42cd061e..3f5e18e8130 100644 --- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json +++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2014-10-16 14:35:41.950175", "doctype": "DocType", "editable_grid": 1, @@ -68,6 +69,7 @@ "description": "Operation completed for how many finished goods?", "fieldname": "completed_qty", "fieldtype": "Float", + "in_list_view": 1, "label": "Completed Qty", "no_copy": 1, "read_only": 1 @@ -188,8 +190,9 @@ } ], "istable": 1, - "modified": "2019-07-16 23:01:07.720337", - "modified_by": "govindsmenokee@gmail.com", + "links": [], + "modified": "2019-12-03 19:24:29.594189", + "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order Operation", "owner": "Administrator", @@ -197,4 +200,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file From 1e7cba9dd39a3890944bacb8bcf31a1fc6342c9c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 24 Mar 2020 15:43:21 +0530 Subject: [PATCH 230/929] fix: Default dimension set code cleanup --- .../public/js/utils/dimension_tree_filter.js | 69 +++++++++---------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index 8b6de0f5179..75c5a820b46 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -1,23 +1,25 @@ frappe.provide('frappe.ui.form'); -erpnext.doctypes_with_dimensions = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset", +let default_dimensions = {}; + +let doctypes_with_dimensions = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset", "Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Shipping Rule", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation", "Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Subscription", "Purchase Order", "Journal Entry", "Material Request", "Purchase Receipt", "Landed Cost Item", "Asset"]; -erpnext.child_docs = ["Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", +let child_docs = ["Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", "Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Landed Cost Item", "Asset Value Adjustment", "Opening Invoice Creation Tool Item", "Subscription Plan"]; frappe.call({ method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimension_filters", - callback: function(r){ + callback: function(r) { erpnext.dimension_filters = r.message[0]; - erpnext.default_dimensions = r.message[1]; + default_dimensions = r.message[1]; } }); -erpnext.doctypes_with_dimensions.forEach((doctype) => { +doctypes_with_dimensions.forEach((doctype) => { frappe.ui.form.on(doctype, { onload: function(frm) { erpnext.dimension_filters.forEach((dimension) => { @@ -27,41 +29,40 @@ erpnext.doctypes_with_dimensions.forEach((doctype) => { "is_group": 0 }); } - - if (Object.keys(erpnext.default_dimensions).length > 0) { - if (frappe.meta.has_field(doctype, dimension['fieldname'])) { - if (frm.is_new() && frappe.meta.has_field(doctype, 'company') && frm.doc.company) { - frm.set_value(dimension['fieldname'], erpnext.default_dimensions[frm.doc.company][dimension['document_type']]); - } - } - - if (frm.doc.items && frm.doc.items.length && frm.doc.docstatus === 0 - && (!frm.doc.items[0][dimension['fieldname']])) { - frm.doc.items[0][dimension['fieldname']] = erpnext.default_dimensions[frm.doc.company][dimension['document_type']]; - } - - if (frm.doc.accounts && frm.doc.accounts.length && frm.doc.docstatus === 0 - && (!frm.doc.items[0][dimension['fieldname']])) { - frm.doc.accounts[0][dimension['fieldname']] = erpnext.default_dimensions[frm.doc.company][dimension['document_type']]; - } - } }); }); }, company: function(frm) { - if(frm.doc.company && (Object.keys(erpnext.default_dimensions).length > 0)) { - erpnext.dimension_filters.forEach((dimension) => { - if (frappe.meta.has_field(doctype, dimension['fieldname'])) { - frm.set_value(dimension['fieldname'], erpnext.default_dimensions[frm.doc.company][dimension['document_type']]); - } - }); + if(frm.doc.company && (Object.keys(default_dimensions || {}).length > 0) + && default_dimensions[frm.doc.company]) { + frm.trigger('update_dimension'); } }, + + update_dimension: function(frm) { + erpnext.dimension_filters.forEach((dimension) => { + if (frm.is_new()) { + if (frm.doc.company && Object.keys(default_dimensions || {}).length > 0 + && default_dimensions[frm.doc.company]) { + + if (frappe.meta.has_field(doctype, dimension['fieldname'])) { + frm.set_value(dimension['fieldname'], + default_dimensions[frm.doc.company][dimension['document_type']]); + } + + $.each(frm.doc.items || frm.doc.accounts || [], function(i, row) { + frappe.model.set_value(row.doctype, row.name, dimension['fieldname'], + default_dimensions[frm.doc.company][dimension['document_type']]) + }); + } + } + }); + } }); }); -erpnext.child_docs.forEach((doctype) => { +child_docs.forEach((doctype) => { frappe.ui.form.on(doctype, { items_add: function(frm, cdt, cdn) { erpnext.dimension_filters.forEach((dimension) => { @@ -77,14 +78,6 @@ erpnext.child_docs.forEach((doctype) => { }); }, - company: function(frm) { - if(frm.doc.company) { - erpnext.dimension_filters.forEach((dimension) => { - frm.set_value(dimension['fieldname'], erpnext.default_dimensions[frm.doc.company][dimension['document_type']]); - }); - } - }, - items_add: function(frm, cdt, cdn) { erpnext.dimension_filters.forEach((dimension) => { var row = frappe.get_doc(cdt, cdn); From 43806f3e68cccf648a5d149d5ad055395a2bf60f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Mar 2020 16:52:07 +0530 Subject: [PATCH 231/929] fix: Place of supply validation in GSTR-1 report --- erpnext/regional/report/gstr_1/gstr_1.py | 27 ++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 4f9cc7ff7a0..e9355f665e5 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -54,8 +54,8 @@ class Gstr1Report(object): return self.columns, self.data def get_data(self): - if self.filters.get("type_of_business") == "B2C Small": - self.get_b2cs_data() + if self.filters.get("type_of_business") in ("B2C Small", "B2C Large"): + self.get_b2c_data() else: for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): invoice_details = self.invoices.get(inv) @@ -69,7 +69,7 @@ class Gstr1Report(object): if taxable_value: self.data.append(row) - def get_b2cs_data(self): + def get_b2c_data(self): b2cs_output = {} for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): @@ -84,7 +84,10 @@ class Gstr1Report(object): "rate": "", "taxable_value": 0, "cess_amount": 0, - "type": "" + "type": "", + "invoice_number": invoice_details.get("invoice_number"), + "posting_date": invoice_details.get("posting_date"), + "invoice_value": invoice_details.get("base_grand_total"), }) row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin)) @@ -139,7 +142,7 @@ class Gstr1Report(object): where docstatus = 1 {where_conditions} order by posting_date desc """.format(select_columns=self.select_columns, doctype=self.doctype, - where_conditions=conditions), self.filters, as_dict=1) + where_conditions=conditions), self.filters, as_dict=1, debug=1) for d in invoice_data: self.invoices.setdefault(d.invoice_number, d) @@ -164,7 +167,7 @@ class Gstr1Report(object): frappe.throw(_("Please set B2C Limit in GST Settings.")) if self.filters.get("type_of_business") == "B2C Large": - conditions += """ and SUBSTR(place_of_supply, 1, 2) != SUBSTR(company_gstin, 1, 2) + conditions += """ and ifnull(SUBSTR(place_of_supply, 1, 2),'') != ifnull(SUBSTR(company_gstin, 1, 2),'') and grand_total > {0} and is_return != 1 and gst_category ='Unregistered' """.format(flt(b2c_limit)) elif self.filters.get("type_of_business") == "B2C Small": @@ -581,6 +584,11 @@ def get_b2b_json(res, gstin): if not gst_in: continue for number, invoice in iteritems(res[gst_in]): + if not invoice[0]["place_of_supply"]: + frappe.throw(_("""{0} not entered in Invoice {1}. + Please update and try again""").format(frappe.bold("Place Of Supply"), + frappe.bold(invoice[0]['invoice_number']))) + inv_item = get_basic_invoice_detail(invoice[0]) inv_item["pos"] = "%02d" % int(invoice[0]["place_of_supply"].split('-')[0]) inv_item["rchrg"] = invoice[0]["reverse_charge"] @@ -606,6 +614,9 @@ def get_b2cs_json(data, gstin): out = [] for d in data: + if not d.get("place_of_supply"): + frappe.throw(_("""{0} not entered in some invoices. + Please update and try again""").format(frappe.bold("Place Of Supply"))) pos = d.get('place_of_supply').split('-')[0] tax_details = {} @@ -642,6 +653,10 @@ def get_b2cs_json(data, gstin): def get_b2cl_json(res, gstin): out = [] for pos in res: + if not pos: + frappe.throw(_("""{0} not entered in some invoices. + Please update and try again""").format(frappe.bold("Place Of Supply"))) + b2cl_item, inv = {"pos": "%02d" % int(pos.split('-')[0]), "inv": []}, [] for row in res[pos]: From 4f1a3876ec8302df49a0336903371297d420e6b3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Mar 2020 16:54:17 +0530 Subject: [PATCH 232/929] fix: Remove debug --- erpnext/regional/report/gstr_1/gstr_1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index e9355f665e5..a4d0bf59c11 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -142,7 +142,7 @@ class Gstr1Report(object): where docstatus = 1 {where_conditions} order by posting_date desc """.format(select_columns=self.select_columns, doctype=self.doctype, - where_conditions=conditions), self.filters, as_dict=1, debug=1) + where_conditions=conditions), self.filters, as_dict=1) for d in invoice_data: self.invoices.setdefault(d.invoice_number, d) From bcb9c285976d99b7082d9afaae836101b276f7f6 Mon Sep 17 00:00:00 2001 From: P-Froggy <60393001+P-Froggy@users.noreply.github.com> Date: Thu, 26 Mar 2020 02:35:09 +0100 Subject: [PATCH 233/929] Fix: Add missing bank accounts reference in supplier dashboard --- erpnext/buying/doctype/supplier/supplier_dashboard.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/supplier/supplier_dashboard.py b/erpnext/buying/doctype/supplier/supplier_dashboard.py index 887a0937369..b3b294d5701 100644 --- a/erpnext/buying/doctype/supplier/supplier_dashboard.py +++ b/erpnext/buying/doctype/supplier/supplier_dashboard.py @@ -9,7 +9,8 @@ def get_data(): 'heatmap_message': _('This is based on transactions against this Supplier. See timeline below for details'), 'fieldname': 'supplier', 'non_standard_fieldnames': { - 'Payment Entry': 'party_name' + 'Payment Entry': 'party_name', + 'Bank Account': 'party' }, 'transactions': [ { @@ -24,6 +25,10 @@ def get_data(): 'label': _('Payments'), 'items': ['Payment Entry'] }, + { + 'label': _('Bank'), + 'items': ['Bank Account'] + }, { 'label': _('Pricing'), 'items': ['Pricing Rule'] From 750985dc23eba0ba9f70590d1c4cdb2a20c989e7 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 26 Mar 2020 13:18:36 +0530 Subject: [PATCH 234/929] feat: auto set batch no on serial no selection (#20758) * feat: auto set batch no on serial no selection * fix: dialog not shown if set warehouse selected * fix: typo * fix: merge conflict * fix: callback no getting called after serial no selected * fix: callback no getting called after serial no selected * fix: available batch qty not fetched without set_warehouse selected * fix: item batch not synced with dialog batch table --- erpnext/public/js/controllers/transaction.js | 15 +- .../js/utils/serial_no_batch_selector.js | 144 +++++++++++------- erpnext/selling/sales_common.js | 9 +- erpnext/stock/doctype/serial_no/serial_no.py | 11 +- 4 files changed, 119 insertions(+), 60 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 146895097c5..ce27c6becce 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -4,7 +4,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ setup: function() { this._super(); - frappe.flags.hide_serial_batch_dialog = false; + frappe.flags.hide_serial_batch_dialog = true; frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); var has_margin_field = frappe.meta.has_field(cdt, 'margin_type'); @@ -506,6 +506,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, () => me.frm.script_manager.trigger("price_list_rate", cdt, cdn), () => me.toggle_conversion_factor(item), + () => { + if (show_batch_dialog) + return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) + .then((r) => { + if(r.message.has_batch_no || r.message.has_serial_no) { + frappe.flags.hide_serial_batch_dialog = false; + } + }); + }, () => { if(show_batch_dialog && !frappe.flags.hide_serial_batch_dialog) { var d = locals[cdt][cdn]; @@ -515,7 +524,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ erpnext.show_serial_batch_selector(me.frm, d, (item) => { me.frm.script_manager.trigger('qty', item.doctype, item.name); - }); + if (!me.frm.doc.set_warehouse) + me.frm.script_manager.trigger('warehouse', item.doctype, item.name); + }, undefined, !frappe.flags.hide_serial_batch_dialog); } }, () => me.conversion_factor(doc, cdt, cdn, true), diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 101ac9cd610..e78ab9fb690 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -5,14 +5,13 @@ erpnext.SerialNoBatchSelector = Class.extend({ this.show_dialog = show_dialog; // frm, item, warehouse_details, has_batch, oldest let d = this.item; - if (d && d.has_batch_no && (!d.batch_no || this.show_dialog)) { - this.has_batch = 1; - this.setup(); + this.has_batch = 0; this.has_serial_no = 0; + + if (d && d.has_batch_no && (!d.batch_no || this.show_dialog)) this.has_batch = 1; // !(this.show_dialog == false) ensures that show_dialog is implictly true, even when undefined - } else if(d && d.has_serial_no && !(this.show_dialog == false)) { - this.has_batch = 0; - this.setup(); - } + if(d && d.has_serial_no && !(this.show_dialog == false)) this.has_serial_no = 1; + + this.setup(); }, setup: function() { @@ -36,16 +35,16 @@ erpnext.SerialNoBatchSelector = Class.extend({ label: __('Item Code'), default: me.item_code }, - {fieldtype:'Column Break'}, { fieldname: 'warehouse', fieldtype:'Link', options: 'Warehouse', + reqd: me.has_batch && !me.has_serial_no ? 0 : 1, label: __(me.warehouse_details.type), - default: me.warehouse_details.name, + default: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '', onchange: function(e) { - if(me.has_batch) { + if(me.has_batch && !me.has_serial_no) { fields = fields.concat(me.get_batch_fields()); } else { fields = fields.concat(me.get_serial_no_fields()); @@ -74,15 +73,16 @@ erpnext.SerialNoBatchSelector = Class.extend({ { fieldname: 'qty', fieldtype:'Float', - read_only: me.has_batch, - label: __(me.has_batch ? 'Total Qty' : 'Qty'), + read_only: me.has_batch && !me.has_serial_no, + label: __(me.has_batch && !me.has_serial_no ? 'Total Qty' : 'Qty'), default: 0 }, { fieldname: 'auto_fetch_button', fieldtype:'Button', - hidden: me.has_batch, - label: __('Fetch based on FIFO'), + hidden: me.has_batch && !me.has_serial_no, + label: __('Auto Fetch'), + description: __('Fetch Serial Numbers based on FIFO'), click: () => { let qty = this.dialog.fields_dict.qty.get_value(); let numbers = frappe.call({ @@ -90,7 +90,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ args: { qty: qty, item_code: me.item_code, - warehouse: me.warehouse_details.name, + warehouse: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '', batch_no: me.item.batch_no || null } }); @@ -109,10 +109,12 @@ erpnext.SerialNoBatchSelector = Class.extend({ } ]; - if (this.has_batch) { + if (this.has_batch && !this.has_serial_no) { title = __("Select Batch Numbers"); fields = fields.concat(this.get_batch_fields()); } else { + // if only serial no OR + // if both batch_no & serial_no then only select serial_no and auto set batches nos title = __("Select Serial Numbers"); fields = fields.concat(this.get_serial_no_fields()); } @@ -122,25 +124,31 @@ erpnext.SerialNoBatchSelector = Class.extend({ fields: fields }); - if (this.item.serial_no) { - this.dialog.fields_dict.serial_no.set_value(this.item.serial_no); - } - this.dialog.set_primary_action(__('Insert'), function() { me.values = me.dialog.get_values(); if(me.validate()) { - me.set_items(); - me.dialog.hide(); + frappe.run_serially([ + () => me.update_batch_items(), + () => me.update_serial_no_item(), + () => me.update_batch_serial_no_items(), + () => { + refresh_field("items"); + if (me.callback) { + return me.callback(me.item); + } + }, + () => me.dialog.hide() + ]) } }); if(this.show_dialog) { let d = this.item; - if (d.has_serial_no && d.serial_no) { - this.dialog.set_value('serial_no', d.serial_no); + if (this.item.serial_no) { + this.dialog.fields_dict.serial_no.set_value(this.item.serial_no); } - - if (d.has_batch_no && d.batch_no) { + + if (this.has_batch && !this.has_serial_no && d.batch_no) { this.frm.doc.items.forEach(data => { if(data.item_code == d.item_code) { this.dialog.fields_dict.batches.df.data.push({ @@ -155,7 +163,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ } } - if (this.has_batch) { + if (this.has_batch && !this.has_serial_no) { this.update_total_qty(); } @@ -174,7 +182,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ frappe.throw(__("Please select a warehouse")); return false; } - if(this.has_batch) { + if(this.has_batch && !this.has_serial_no) { if(values.batches.length === 0 || !values.batches) { frappe.throw(__("Please select batches for batched item " + values.item_code)); @@ -193,34 +201,23 @@ erpnext.SerialNoBatchSelector = Class.extend({ } else { let serial_nos = values.serial_no || ''; if (!serial_nos || !serial_nos.replace(/\s/g, '').length) { - if (!this.show_dialog) { - frappe.throw(__("Please enter serial numbers for serialized item " - + values.item_code)); - return false; - } + frappe.throw(__("Please enter serial numbers for serialized item " + + values.item_code)); + return false; } return true; } }, - set_items: function() { - var me = this; - if(this.has_batch) { + update_batch_items() { + // clones an items if muliple batches are selected. + if(this.has_batch && !this.has_serial_no) { this.values.batches.map((batch, i) => { let batch_no = batch.batch_no; let row = ''; if (i !== 0 && !this.batch_exists(batch_no)) { - row = this.frm.add_child("items", { - 'item_code': this.item.item_code, - 'item_name': this.item.item_name, - 'price_list_rate': this.item.price_list_rate, - 'rate': this.item.rate, - 'qty': batch.selected_qty, - 'batch_no': batch_no, - 'actual_qty': this.item.actual_qty, - 'discount_percentage': this.item.discount_percentage - }); + row = this.frm.add_child("items", { ...this.item }); } else { row = this.frm.doc.items.find(i => i.batch_no === batch_no); } @@ -228,16 +225,59 @@ erpnext.SerialNoBatchSelector = Class.extend({ if (!row) { row = this.item; } - + // this ensures that qty & batch no is set this.map_row_values(row, batch, 'batch_no', 'selected_qty', this.values.warehouse); }); - } else { + } + }, + + update_serial_no_item() { + // just updates serial no for the item + if(this.has_serial_no && !this.has_batch) { this.map_row_values(this.item, this.values, 'serial_no', 'qty'); } + }, - refresh_field("items"); - this.callback && this.callback(this.item); + update_batch_serial_no_items() { + // if serial no selected is from different batches, adds new rows for each batch. + if(this.has_batch && this.has_serial_no) { + const selected_serial_nos = this.values.serial_no.split(/\n/g).filter(s => s); + + return frappe.db.get_list("Serial No", { + filters: { 'name': ["in", selected_serial_nos]}, + fields: ["batch_no", "name"] + }).then((data) => { + // data = [{batch_no: 'batch-1', name: "SR-001"}, + // {batch_no: 'batch-2', name: "SR-003"}, {batch_no: 'batch-2', name: "SR-004"}] + const batch_serial_map = data.reduce((acc, d) => { + if (!acc[d['batch_no']]) acc[d['batch_no']] = []; + acc[d['batch_no']].push(d['name']) + return acc + }, {}) + // batch_serial_map = { "batch-1": ['SR-001'], "batch-2": ["SR-003", "SR-004"]} + Object.keys(batch_serial_map).map((batch_no, i) => { + let row = ''; + const serial_no = batch_serial_map[batch_no]; + if (i == 0) { + row = this.item; + this.map_row_values(row, {qty: serial_no.length, batch_no: batch_no}, 'batch_no', + 'qty', this.values.warehouse); + } else if (!this.batch_exists(batch_no)) { + row = this.frm.add_child("items", { ...this.item }); + row.batch_no = batch_no; + } else { + row = this.frm.doc.items.find(i => i.batch_no === batch_no); + } + const values = { + 'qty': serial_no.length, + 'serial_no': serial_no.join('\n') + } + this.map_row_values(row, values, 'serial_no', + 'qty', this.values.warehouse); + }); + }) + } }, batch_exists: function(batch) { @@ -287,7 +327,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ return { filters: { item_code: me.item_code, - warehouse: me.warehouse || me.warehouse_details.name + warehouse: me.warehouse || typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '' }, query: 'erpnext.controllers.queries.get_batch_no' }; @@ -447,7 +487,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ { fieldname: 'serial_no', fieldtype: 'Small Text', - label: __(me.has_batch ? 'Selected Batch Numbers' : 'Selected Serial Numbers'), + label: __(me.has_batch && !me.has_serial_no ? 'Selected Batch Numbers' : 'Selected Serial Numbers'), onchange: function() { me.serial_list = this.get_value() .replace(/\n/g, ' ').match(/\S+/g) || []; diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 8278745a804..af100692c6f 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -413,15 +413,20 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ */ set_batch_number: function(cdt, cdn) { const doc = frappe.get_doc(cdt, cdn); - if (doc && doc.has_batch_no) { + if (doc && doc.has_batch_no && doc.warehouse) { this._set_batch_number(doc); } }, _set_batch_number: function(doc) { + let args = {'item_code': doc.item_code, 'warehouse': doc.warehouse, 'qty': flt(doc.qty) * flt(doc.conversion_factor)}; + if (doc.has_serial_no && doc.serial_no) { + args['serial_no'] = doc.serial_no + } + return frappe.call({ method: 'erpnext.stock.doctype.batch.batch.get_batch_no', - args: {'item_code': doc.item_code, 'warehouse': doc.warehouse, 'qty': flt(doc.qty) * flt(doc.conversion_factor)}, + args: args, callback: function(r) { if(r.message) { frappe.model.set_value(doc.doctype, doc.name, 'batch_no', r.message); diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 31f84c9aaed..cc186f2dbe7 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -503,12 +503,15 @@ def get_delivery_note_serial_no(item_code, qty, delivery_note): return serial_nos @frappe.whitelist() -def auto_fetch_serial_number(qty, item_code, warehouse, batch_no=None): - serial_numbers = frappe.get_list("Serial No", filters={ +def auto_fetch_serial_number(qty, item_code, warehouse, batch_nos=None): + import json + filters = { "item_code": item_code, "warehouse": warehouse, - "batch_no": batch_no, "delivery_document_no": "", "sales_invoice": "" - }, limit=qty, order_by="creation") + } + if batch_nos: filters["batch_no"] = ["in", json.loads(batch_nos)] + + serial_numbers = frappe.get_list("Serial No", filters=filters, limit=qty, order_by="creation") return [item['name'] for item in serial_numbers] From b9f81d913b605228737d8f2c8e8ee63133fc9a9a Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 26 Mar 2020 13:20:27 +0530 Subject: [PATCH 235/929] fix: currency not fetched on quotation creation (#20998) --- erpnext/selling/doctype/customer/customer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 791a3e1536a..4fca05ba7ea 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -231,9 +231,11 @@ def make_quotation(source_name, target_doc=None): target_doc.run_method("set_other_charges") target_doc.run_method("calculate_taxes_and_totals") - price_list = frappe.get_value("Customer", source_name, "default_price_list") + price_list, currency = frappe.db.get_value("Customer", source_name, ['default_price_list', 'default_currency']) if price_list: target_doc.selling_price_list = price_list + if currency: + target_doc.currency = currency return target_doc From 01f80b239de1aa87e96d4753a90b8805c032e77a Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 26 Mar 2020 13:23:06 +0530 Subject: [PATCH 236/929] fix: wrong calculation of depreciation eliminated for a period (#21032) --- .../asset_depreciations_and_balances.py | 213 +++++++++--------- 1 file changed, 112 insertions(+), 101 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 0c99f1424cf..d7efbad240d 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -4,126 +4,137 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import formatdate, getdate, flt, add_days +from frappe.utils import formatdate, flt, add_days + def execute(filters=None): filters.day_before_from_date = add_days(filters.from_date, -1) columns, data = get_columns(filters), get_data(filters) return columns, data - + + def get_data(filters): data = [] - + asset_categories = get_asset_categories(filters) assets = get_assets(filters) - asset_costs = get_asset_costs(assets, filters) - asset_depreciations = get_accumulated_depreciations(assets, filters) - + for asset_category in asset_categories: row = frappe._dict() - row.asset_category = asset_category - row.update(asset_costs.get(asset_category)) + # row.asset_category = asset_category + row.update(asset_category) + + row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase) - + flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset)) + + row.update(next(asset for asset in assets if asset["asset_category"] == asset_category.get("asset_category", ""))) + row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) + + flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated_during_the_period)) + + row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) - + flt(row.accumulated_depreciation_as_on_from_date)) + + row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) - + flt(row.accumulated_depreciation_as_on_to_date)) - row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase) - - flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset)) - - row.update(asset_depreciations.get(asset_category)) - row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) + - flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated)) - - row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) - - flt(row.accumulated_depreciation_as_on_from_date)) - - row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) - - flt(row.accumulated_depreciation_as_on_to_date)) - data.append(row) - + return data - + + def get_asset_categories(filters): - return frappe.db.sql_list(""" - select distinct asset_category from `tabAsset` - where docstatus=1 and company=%s and purchase_date <= %s - """, (filters.company, filters.to_date)) - + return frappe.db.sql(""" + SELECT asset_category, + ifnull(sum(case when purchase_date < %(from_date)s then + case when ifnull(disposal_date, 0) = 0 or disposal_date >= %(from_date)s then + gross_purchase_amount + else + 0 + end + else + 0 + end), 0) as cost_as_on_from_date, + ifnull(sum(case when purchase_date >= %(from_date)s then + gross_purchase_amount + else + 0 + end), 0) as cost_of_new_purchase, + ifnull(sum(case when ifnull(disposal_date, 0) != 0 + and disposal_date >= %(from_date)s + and disposal_date <= %(to_date)s then + case when status = "Sold" then + gross_purchase_amount + else + 0 + end + else + 0 + end), 0) as cost_of_sold_asset, + ifnull(sum(case when ifnull(disposal_date, 0) != 0 + and disposal_date >= %(from_date)s + and disposal_date <= %(to_date)s then + case when status = "Scrapped" then + gross_purchase_amount + else + 0 + end + else + 0 + end), 0) as cost_of_scrapped_asset + from `tabAsset` + where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s + group by asset_category + """, {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1) + def get_assets(filters): return frappe.db.sql(""" - select name, asset_category, purchase_date, gross_purchase_amount, disposal_date, status - from `tabAsset` - where docstatus=1 and company=%s and purchase_date <= %s""", - (filters.company, filters.to_date), as_dict=1) - -def get_asset_costs(assets, filters): - asset_costs = frappe._dict() - for d in assets: - asset_costs.setdefault(d.asset_category, frappe._dict({ - "cost_as_on_from_date": 0, - "cost_of_new_purchase": 0, - "cost_of_sold_asset": 0, - "cost_of_scrapped_asset": 0 - })) - - costs = asset_costs[d.asset_category] - - if getdate(d.purchase_date) < getdate(filters.from_date): - if not d.disposal_date or getdate(d.disposal_date) >= getdate(filters.from_date): - costs.cost_as_on_from_date += flt(d.gross_purchase_amount) - else: - costs.cost_of_new_purchase += flt(d.gross_purchase_amount) - - if d.disposal_date and getdate(d.disposal_date) >= getdate(filters.from_date) \ - and getdate(d.disposal_date) <= getdate(filters.to_date): - if d.status == "Sold": - costs.cost_of_sold_asset += flt(d.gross_purchase_amount) - elif d.status == "Scrapped": - costs.cost_of_scrapped_asset += flt(d.gross_purchase_amount) - - return asset_costs - -def get_accumulated_depreciations(assets, filters): - asset_depreciations = frappe._dict() - for d in assets: - asset = frappe.get_doc("Asset", d.name) - - if d.asset_category in asset_depreciations: - asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] += asset.opening_accumulated_depreciation - else: - asset_depreciations.setdefault(d.asset_category, frappe._dict({ - "accumulated_depreciation_as_on_from_date": asset.opening_accumulated_depreciation, - "depreciation_amount_during_the_period": 0, - "depreciation_eliminated_during_the_period": 0 - })) + SELECT results.asset_category, + sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date, + sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, + sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period + from (SELECT a.asset_category, + ifnull(sum(case when ds.schedule_date < %(from_date)s then + ds.depreciation_amount + else + 0 + end), 0) as accumulated_depreciation_as_on_from_date, + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s + and a.disposal_date <= %(to_date)s and ds.schedule_date <= a.disposal_date then + ds.depreciation_amount + else + 0 + end), 0) as depreciation_eliminated_during_the_period, + ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s + and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then + ds.depreciation_amount + else + 0 + end), 0) as depreciation_amount_during_the_period + from `tabAsset` a, `tabDepreciation Schedule` ds + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent + group by a.asset_category + union + SELECT a.asset_category, + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 + and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) + then + 0 + else + a.opening_accumulated_depreciation + end), 0) as accumulated_depreciation_as_on_from_date, + ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then + a.opening_accumulated_depreciation + else + 0 + end), 0) as depreciation_eliminated_during_the_period, + 0 as depreciation_amount_during_the_period + from `tabAsset` a + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s + group by a.asset_category) as results + group by results.asset_category + """, {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1) - depr = asset_depreciations[d.asset_category] - if not asset.schedules: # if no schedule, - if asset.disposal_date: - # and disposal is NOT within the period, then opening accumulated depreciation not included - if getdate(asset.disposal_date) < getdate(filters.from_date) or getdate(asset.disposal_date) > getdate(filters.to_date): - asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] = 0 - - # if no schedule, and disposal is within period, accumulated dep is the amount eliminated - if getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date): - depr.depreciation_eliminated_during_the_period += asset.opening_accumulated_depreciation - - for schedule in asset.get("schedules"): - if getdate(schedule.schedule_date) < getdate(filters.from_date): - if not asset.disposal_date or getdate(asset.disposal_date) >= getdate(filters.from_date): - depr.accumulated_depreciation_as_on_from_date += flt(schedule.depreciation_amount) - elif getdate(schedule.schedule_date) <= getdate(filters.to_date): - if not asset.disposal_date: - depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount) - else: - if getdate(schedule.schedule_date) <= getdate(asset.disposal_date): - depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount) - - if asset.disposal_date and getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date): - if getdate(schedule.schedule_date) <= getdate(asset.disposal_date): - depr.depreciation_eliminated_during_the_period += flt(schedule.depreciation_amount) - - return asset_depreciations - def get_columns(filters): return [ { From 6b038bda0de5c7a81e942bec6602f244e02fd28b Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 26 Mar 2020 13:23:54 +0530 Subject: [PATCH 237/929] fix: Project field in Stock Entry Detail should be editable (#21000) --- .../doctype/stock_entry_detail/stock_entry_detail.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 d86e68b7222..a848c80cf2c 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-03-29 18:22:12", "doctype": "DocType", @@ -479,8 +480,7 @@ "fieldname": "project", "fieldtype": "Link", "label": "Project", - "options": "Project", - "read_only": 1 + "options": "Project" }, { "fieldname": "po_detail", @@ -494,7 +494,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-08-20 14:01:02.319754", + "links": [], + "modified": "2020-03-19 12:34:09.836295", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", From 72430e91b267b5cc8fca9913862dba6ceace772d Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 26 Mar 2020 08:55:14 +0100 Subject: [PATCH 238/929] fix: account groups (#21046) --- ..._kontenplan_SKR04_with_account_number.json | 387 ++++++++---------- 1 file changed, 177 insertions(+), 210 deletions(-) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json index 275374a4b70..4ee12209caa 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json @@ -2417,29 +2417,26 @@ "Erl\u00f6se aus Verk\u00e4ufen Sachanlageverm\u00f6gen (bei Buchgewinn)": { "account_number": "4849" }, - "Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn) (Gruppe)": { - "is_group": 1, - "Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn)": { - "account_number": "4850" - }, - "Erl\u00f6se aus Verk\u00e4ufen Finanzanlagen (bei Buchgewinn)": { - "account_number": "4851" - }, - "Erl\u00f6se aus Verk\u00e4ufen Finanzanlagen (inl\u00e4ndische Kap.Ges., bei Buchgewinn)": { - "account_number": "4852" - }, - "Anlagenabg\u00e4nge Sachanlagen (Restbuchwert bei Buchvergewinn)": { - "account_number": "4855" - }, - "Anlagenabg\u00e4nge immaterielle VG (Restbuchwert bei Buchgewinn)": { - "account_number": "4856" - }, - "Anlagenabg\u00e4nge Finanzanlagen (Restbuchwert bei Buchgewinn)": { - "account_number": "4857" - }, - "Anlagenabg\u00e4nge Finanzanlagen (inl\u00e4ndische Kap.Ges., Restbuchwert bei Buchgewinn)": { - "account_number": "4858" - } + "Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn)": { + "account_number": "4850" + }, + "Erl\u00f6se aus Verk\u00e4ufen Finanzanlagen (bei Buchgewinn)": { + "account_number": "4851" + }, + "Erl\u00f6se aus Verk\u00e4ufen Finanzanlagen (inl\u00e4ndische Kap.Ges., bei Buchgewinn)": { + "account_number": "4852" + }, + "Anlagenabg\u00e4nge Sachanlagen (Restbuchwert bei Buchvergewinn)": { + "account_number": "4855" + }, + "Anlagenabg\u00e4nge immaterielle VG (Restbuchwert bei Buchgewinn)": { + "account_number": "4856" + }, + "Anlagenabg\u00e4nge Finanzanlagen (Restbuchwert bei Buchgewinn)": { + "account_number": "4857" + }, + "Anlagenabg\u00e4nge Finanzanlagen (inl\u00e4ndische Kap.Ges., Restbuchwert bei Buchgewinn)": { + "account_number": "4858" }, "Ertr\u00e4ge aus Zuschreibungen des Sachanlageverm\u00f6gens": { "account_number": "4910", @@ -2562,20 +2559,17 @@ "Entnahme von Gegenst\u00e4nden ohne USt": { "account_number": "4605" }, - "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt (Gruppe)": { - "is_group": 1, - "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt": { - "account_number": "4630" - }, - "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt": { - "account_number": "4637" - }, - "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternnehmens ohne USt (Telefon-Nutzung)": { - "account_number": "4638" - }, - "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt (Kfz-Nutzung)": { - "account_number": "4639" - } + "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt": { + "account_number": "4630" + }, + "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt": { + "account_number": "4637" + }, + "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternnehmens ohne USt (Telefon-Nutzung)": { + "account_number": "4638" + }, + "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt (Kfz-Nutzung)": { + "account_number": "4639" }, "Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 19 % USt (Gruppe)": { "is_group": 1, @@ -2613,14 +2607,11 @@ "Unentgeltliche Zuwendung von Gegenst\u00e4nden ohne USt": { "account_number": "4689" }, - "Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze) (Gruppe)": { - "is_group": 1, - "Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze)": { - "account_number": "4690" - }, - "Umsatzsteuerverg\u00fctungen, z.B. nach \u00a7 24 UStG": { - "account_number": "4695" - } + "Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze)": { + "account_number": "4690" + }, + "Umsatzsteuerverg\u00fctungen, z.B. nach \u00a7 24 UStG": { + "account_number": "4695" }, "Au\u00dferordentliche Ertr\u00e4ge (Gruppe)": { "is_group": 1, @@ -2630,41 +2621,35 @@ "Au\u00dferordentliche Ertr\u00e4ge finanzwirksam": { "account_number": "7401" }, - "Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam (Gruppe)": { - "is_group": 1, - "Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam": { - "account_number": "7450" - }, - "Ertr\u00e4ge durch Verschmelzung und Umwandlung": { - "account_number": "7451" - }, - "Ertr\u00e4ge durch den Verkauf von bedeutenden Beteiligungen": { - "account_number": "7452" - }, - "Ert\u00e4ge durch den Verkauf von bedeutenden Grundst\u00fccken": { - "account_number": "7453" - }, - "Gewinn aus der Ver\u00e4u\u00dferung oder der Aufgabe von Gesch\u00e4ftsaktivit\u00e4ten nach Steuern": { - "account_number": "7454" - } + "Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam": { + "account_number": "7450" }, - "Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften (Gruppe)": { - "is_group": 1, - "Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften": { - "account_number": "7460" - }, - "Au\u00dferordentliche Ertr\u00e4ge: Zuschreibung f. Sachanlageverm\u00f6gen": { - "account_number": "7461" - }, - "Au\u00dferordentliche Ertr\u00e4ge: Zuschreibung f. Finanzanlageverm\u00f6gen": { - "account_number": "7462" - }, - "Au\u00dferordentliche Ertr\u00e4ge: Wertpapiere im Umlaufverm\u00f6gen": { - "account_number": "7463" - }, - "Au\u00dferordentliche Ertr\u00e4ge: latente Steuern": { - "account_number": "7464" - } + "Ertr\u00e4ge durch Verschmelzung und Umwandlung": { + "account_number": "7451" + }, + "Ertr\u00e4ge durch den Verkauf von bedeutenden Beteiligungen": { + "account_number": "7452" + }, + "Ert\u00e4ge durch den Verkauf von bedeutenden Grundst\u00fccken": { + "account_number": "7453" + }, + "Gewinn aus der Ver\u00e4u\u00dferung oder der Aufgabe von Gesch\u00e4ftsaktivit\u00e4ten nach Steuern": { + "account_number": "7454" + }, + "Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften": { + "account_number": "7460" + }, + "Au\u00dferordentliche Ertr\u00e4ge: Zuschreibung f. Sachanlageverm\u00f6gen": { + "account_number": "7461" + }, + "Au\u00dferordentliche Ertr\u00e4ge: Zuschreibung f. Finanzanlageverm\u00f6gen": { + "account_number": "7462" + }, + "Au\u00dferordentliche Ertr\u00e4ge: Wertpapiere im Umlaufverm\u00f6gen": { + "account_number": "7463" + }, + "Au\u00dferordentliche Ertr\u00e4ge: latente Steuern": { + "account_number": "7464" } } }, @@ -2702,40 +2687,43 @@ }, "Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 16 % USt": { "account_number": "4729" + } + }, + "Gew\u00e4hrte Skonti (Gruppe)": { + "is_group": 1, + "Gew. Skonti": { + "account_number": "4730" }, - "Gew\u00e4hrte Skonti (Gruppe)": { - "is_group": 1, - "Gew. Skonti": { - "account_number": "4730" - }, - "Gew. Skonti 7 % USt": { - "account_number": "4731" - }, - "Gew. Skonti 19 % USt": { - "account_number": "4736" - }, - "Gew. Skonti aus Lieferungen von Mobilfunkger./Schaltkr., f. die der Leistungsempf. die Ust. schuldet": { - "account_number": "4738" - }, - "Gew. Skonti aus Leistungen, f. die der Leistungsempf. die Umsatzsteuer nach \u00a7 13b UStG schuldet": { - "account_number": "4741" - }, - "Gew. Skonti aus Erl\u00f6sen aus im anderen EU-Land steuerpfl. Leistungen, f. die der Leistungsempf. die Ust. schuldet": { - "account_number": "4742" - }, - "Gew. Skonti aus steuerfreien innergem. Lieferungen \u00a7 4 Nr. 1b UStG": { - "account_number": "4743" - }, - "Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen": { - "account_number": "4745" - }, - "Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 7% USt": { - "account_number": "4746" - }, - "Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 19% USt": { - "account_number": "4748" - } + "Gew. Skonti 7 % USt": { + "account_number": "4731" }, + "Gew. Skonti 19 % USt": { + "account_number": "4736" + }, + "Gew. Skonti aus Lieferungen von Mobilfunkger./Schaltkr., f. die der Leistungsempf. die Ust. schuldet": { + "account_number": "4738" + }, + "Gew. Skonti aus Leistungen, f. die der Leistungsempf. die Umsatzsteuer nach \u00a7 13b UStG schuldet": { + "account_number": "4741" + }, + "Gew. Skonti aus Erl\u00f6sen aus im anderen EU-Land steuerpfl. Leistungen, f. die der Leistungsempf. die Ust. schuldet": { + "account_number": "4742" + }, + "Gew. Skonti aus steuerfreien innergem. Lieferungen \u00a7 4 Nr. 1b UStG": { + "account_number": "4743" + }, + "Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen": { + "account_number": "4745" + }, + "Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 7% USt": { + "account_number": "4746" + }, + "Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 19% USt": { + "account_number": "4748" + } + }, + "Gew\u00e4hrte Boni (Gruppe)": { + "is_group": 1, "Gew\u00e4hrte Boni 7 % USt": { "account_number": "4750" }, @@ -2848,103 +2836,79 @@ "account_number": "6398" } }, - "Versicherungen (Gruppe)": { - "is_group": 1, - "Versicherungen": { - "account_number": "6400" - }, - "Versicherungen f. Geb\u00e4ude, die zum Betriebsverm\u00f6gen geh\u00f6ren": { - "account_number": "6405" - }, - "Netto-Pr\u00e4mie f. R\u00fcckdeckung k\u00fcnftiger Versorgungsleistungen": { - "account_number": "6410" - }, - "Beitr\u00e4ge": { - "account_number": "6420" - }, - "Sonstige Abgaben": { - "account_number": "6430" - }, - "Steuerlich abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": { - "account_number": "6436" - }, - "Steuerlich nicht abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": { - "account_number": "6437" - }, - "Ausgleichsabgabe i. S. d. Schwerbehindertengesetzes": { - "account_number": "6440" - }, - "Reparaturen und Instandhaltung von Bauten": { - "account_number": "6450" - }, - "Reparaturen und Instandhaltung von technischenAnlagen und Maschinen": { - "account_number": "6460" - }, - "Reparaturen und Instandhaltung von anderen Anlagen und Betriebs- und Gesch\u00e4ftsausstattung": { - "account_number": "6470" - }, - "Zuf\u00fchrung zu Aufwandsr\u00fcckstellungen": { - "account_number": "6475" - }, - "Reparaturen und Instandhaltung von anderen Anlagen": { - "account_number": "6485" - }, - "Sonstige Reparaturen und Instandhaltungen": { - "account_number": "6490" - }, - "Wartungskosten f. Hard- und Software": { - "account_number": "6495" - }, - "Mietleasing (bewegliche Wirtschaftsg\u00fcter)": { - "account_number": "6498" - } + "Versicherungen": { + "account_number": "6400" + }, + "Versicherungen f. Geb\u00e4ude, die zum Betriebsverm\u00f6gen geh\u00f6ren": { + "account_number": "6405" + }, + "Netto-Pr\u00e4mie f. R\u00fcckdeckung k\u00fcnftiger Versorgungsleistungen": { + "account_number": "6410" + }, + "Beitr\u00e4ge": { + "account_number": "6420" + }, + "Sonstige Abgaben": { + "account_number": "6430" + }, + "Steuerlich abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": { + "account_number": "6436" + }, + "Steuerlich nicht abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": { + "account_number": "6437" + }, + "Ausgleichsabgabe i. S. d. Schwerbehindertengesetzes": { + "account_number": "6440" + }, + "Reparaturen und Instandhaltung von Bauten": { + "account_number": "6450" + }, + "Reparaturen und Instandhaltung von technischenAnlagen und Maschinen": { + "account_number": "6460" + }, + "Reparaturen und Instandhaltung von anderen Anlagen und Betriebs- und Gesch\u00e4ftsausstattung": { + "account_number": "6470" + }, + "Zuf\u00fchrung zu Aufwandsr\u00fcckstellungen": { + "account_number": "6475" + }, + "Reparaturen und Instandhaltung von anderen Anlagen": { + "account_number": "6485" + }, + "Sonstige Reparaturen und Instandhaltungen": { + "account_number": "6490" + }, + "Wartungskosten f. Hard- und Software": { + "account_number": "6495" + }, + "Mietleasing (bewegliche Wirtschaftsg\u00fcter)": { + "account_number": "6498" }, "Fahrzeugkosten (Gruppe)": { "is_group": 1, "Fahrzeugkosten": { "account_number": "6500" }, - "Kfz-Versicherungen (Gruppe)": { - "is_group": 1, - "Kfz-Versicherungen": { - "account_number": "6520" - } + "Kfz-Versicherungen": { + "account_number": "6520" }, - "Laufende Kfz-Betriebskosten (Gruppe)": { - "is_group": 1, - "Laufende Kfz-Betriebskosten": { - "account_number": "6530" - } + "Laufende Kfz-Betriebskosten": { + "account_number": "6530" }, - "Kfz-Reparaturen (Gruppe)": { - "is_group": 1, - "Kfz-Reparaturen": { - "account_number": "6540" - } + "Kfz-Reparaturen": { + "account_number": "6540" }, - "Garagenmiete (Gruppe)": { - "is_group": 1, - "Garagenmiete": { - "account_number": "6550" - } + "Garagenmiete": { + "account_number": "6550" }, - "Mietleasing Kfz (Gruppe)": { - "is_group": 1, - "Mietleasing Kfz": { - "account_number": "6560" - } + "Mietleasing Kfz": { + "account_number": "6560" }, - "Sonstige Kfz-Kosten (Gruppe)": { - "is_group": 1, - "Sonstige Kfz-Kosten": { - "account_number": "6570" - } + "Sonstige Kfz-Kosten": { + "account_number": "6570" }, - "Mautgeb\u00fchren (Gruppe)": { - "is_group": 1, - "Mautgeb\u00fchren": { - "account_number": "6580" - } + "Mautgeb\u00fchren": { + "account_number": "6580" }, "Kfz-Kosten f. betrieblich genutzte zum Privatverm\u00f6gen geh\u00f6rende Kraftfahrzeuge": { "account_number": "6590" @@ -3006,20 +2970,23 @@ "Nicht abzugsf\u00e4hige Betriebsausgaben aus Werbe- und Repr\u00e4sentationskosten": { "account_number": "6645" }, - "Reisekosten Arbeitnehmer": { - "account_number": "6650" - }, - "Reisekosten Arbeitnehmer \u00dcbernachtungsaufwand": { - "account_number": "6660" - }, - "Reisekosten Arbeitnehmer Fahrtkosten": { - "account_number": "6663" - }, - "Reisekosten Arbeitnehmer Verpflegungsmehraufwand": { - "account_number": "6664" - }, - "Kilometergelderstattung Arbeitnehmer": { - "account_number": "6668" + "Reisekosten Arbeitnehmer (Gruppe)": { + "is_group": 1, + "Reisekosten Arbeitnehmer": { + "account_number": "6650" + }, + "Reisekosten Arbeitnehmer \u00dcbernachtungsaufwand": { + "account_number": "6660" + }, + "Reisekosten Arbeitnehmer Fahrtkosten": { + "account_number": "6663" + }, + "Reisekosten Arbeitnehmer Verpflegungsmehraufwand": { + "account_number": "6664" + }, + "Kilometergelderstattung Arbeitnehmer": { + "account_number": "6668" + } }, "Reisekosten Unternehmer (Gruppe)": { "is_group": 1, From 3c378aaac743ded787365019d426970bfbb0cc46 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 26 Mar 2020 13:28:42 +0530 Subject: [PATCH 239/929] fix: Make shelf life mandatory in Batched Item if it has expiry date (#21045) --- erpnext/stock/doctype/batch/batch.py | 7 +++++-- erpnext/stock/doctype/item/item.json | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 8ae978eaf05..9b7249e66b9 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -122,8 +122,11 @@ class Batch(Document): self.expiry_date = add_days(self.manufacturing_date, shelf_life_in_days) if has_expiry_date and not self.expiry_date: - frappe.msgprint(_('Expiry date is mandatory for selected item.')) - frappe.throw(_("Set item's shelf life in days, to set expiry based on manufacturing date plus shelf-life.")) + frappe.throw(msg=_("Please set {0} for Batched Item {1}, which is used to set {2} on Submit.") \ + .format(frappe.bold("Shelf Life in Days"), + frappe.utils.get_link_to_form("Item", self.item), + frappe.bold("Batch Expiry Date")), + title=_("Expiry Date Mandatory")) def get_name_from_naming_series(self): """ diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 3503e7cc1c5..aa6b2fedd7c 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -343,7 +343,8 @@ { "fieldname": "shelf_life_in_days", "fieldtype": "Int", - "label": "Shelf Life In Days" + "label": "Shelf Life In Days", + "mandatory_depends_on": "eval:doc.has_batch_no && doc.has_expiry_date" }, { "default": "2099-12-31", @@ -1045,7 +1046,7 @@ "image_field": "image", "links": [], "max_attachments": 1, - "modified": "2020-01-02 19:13:59.295963", + "modified": "2020-03-24 16:14:36.950677", "modified_by": "Administrator", "module": "Stock", "name": "Item", From 8a5587749b5f8c73bc72831d4e223911791ed426 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 26 Mar 2020 13:29:36 +0530 Subject: [PATCH 240/929] fix: Added hidden GL column in general ledger (#21022) Co-authored-by: Nabin Hait --- erpnext/accounts/report/general_ledger/general_ledger.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index f45911f90e5..6c9dec90f8c 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -131,7 +131,7 @@ def get_gl_entries(filters): gl_entries = frappe.db.sql( """ select - posting_date, account, party_type, party, + name as gl_entry, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center, project, against_voucher_type, against_voucher, account_currency, remarks, against, is_opening {select_fields} @@ -362,6 +362,12 @@ def get_columns(filters): currency = get_company_currency(company) columns = [ + { + "fieldname": "gl_entry", + "fieldtype": "Link", + "options": "GL Entry", + "hidden": 1 + }, { "label": _("Posting Date"), "fieldname": "posting_date", From ff49a2f0b61483b0614018b902b9b206ffdaa623 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 26 Mar 2020 13:36:21 +0530 Subject: [PATCH 241/929] fix: Ignored user permission for parent_company and existing_company field in Company (#21010) --- erpnext/setup/doctype/company/company.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 5ce51a6e8d9..3eb8949a5fa 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -157,6 +157,7 @@ { "fieldname": "parent_company", "fieldtype": "Link", + "ignore_user_permissions": 1, "in_list_view": 1, "label": "Parent Company", "options": "Company" @@ -277,6 +278,7 @@ "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"", "fieldname": "existing_company", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Existing Company ", "no_copy": 1, "options": "Company" @@ -728,7 +730,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2020-03-18 18:26:03.145312", + "modified": "2020-03-22 18:26:03.145312", "modified_by": "Administrator", "module": "Setup", "name": "Company", From c944676bb5cef15a535494ac7808f97e386b5639 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 26 Mar 2020 14:49:28 +0530 Subject: [PATCH 242/929] fix: Updated Bin Requested Qty logic --- erpnext/patches.txt | 1 + .../v12_0/recalculate_requested_qty_in_bin.py | 13 ++++++++++++ erpnext/stock/stock_balance.py | 21 ++++++++++++++----- 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index de4c89c5ace..9850a70410c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -651,3 +651,4 @@ erpnext.patches.v12_0.add_export_type_field_in_party_master erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22 erpnext.patches.v12_0.create_irs_1099_field_united_states erpnext.patches.v12_0.set_permission_einvoicing +erpnext.patches.v12_0.recalculate_requested_qty_in_bin \ No newline at end of file diff --git a/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py b/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py new file mode 100644 index 00000000000..8267df95e11 --- /dev/null +++ b/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +import frappe +from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty + +def execute(): + bin_details = frappe.db.sql(""" + SELECT item_code, warehouse + FROM `tabBin`""",as_dict=1) + + for entry in bin_details: + update_bin_qty(entry.get("item_code"), entry.get("warehouse"), { + "indented_qty": get_indented_qty(entry.get("item_code"), entry.get("warehouse")) + }) \ No newline at end of file diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py index e5dc6b12df7..2bdb04ed2c9 100644 --- a/erpnext/stock/stock_balance.py +++ b/erpnext/stock/stock_balance.py @@ -113,13 +113,24 @@ def get_reserved_qty(item_code, warehouse): return flt(reserved_qty[0][0]) if reserved_qty else 0 def get_indented_qty(item_code, warehouse): - indented_qty = frappe.db.sql("""select sum((mr_item.qty - mr_item.ordered_qty) * mr_item.conversion_factor) + inward_qty = frappe.db.sql("""select sum((mr_item.qty - mr_item.ordered_qty) * mr_item.conversion_factor) + from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr + where mr_item.item_code=%s and mr_item.warehouse=%s + and mr.material_request_type in ('Purchase', 'Manufacture') + and mr_item.qty > mr_item.ordered_qty and mr_item.parent=mr.name + and mr.status!='Stopped' and mr.docstatus=1""", (item_code, warehouse)) + + outward_qty = frappe.db.sql("""select sum((mr_item.qty - mr_item.ordered_qty) * mr_item.conversion_factor) from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr where mr_item.item_code=%s and mr_item.warehouse=%s + and mr.material_request_type in ('Material Issue', 'Material Transfer') and mr_item.qty > mr_item.ordered_qty and mr_item.parent=mr.name and mr.status!='Stopped' and mr.docstatus=1""", (item_code, warehouse)) - return flt(indented_qty[0][0]) if indented_qty else 0 + inward_qty, outward_qty = flt(inward_qty[0][0]) if inward_qty else 0, flt(outward_qty[0][0]) if outward_qty else 0 + indented_qty = inward_qty - outward_qty + + return indented_qty def get_ordered_qty(item_code, warehouse): ordered_qty = frappe.db.sql(""" @@ -145,9 +156,9 @@ def update_bin_qty(item_code, warehouse, qty_dict=None): from erpnext.stock.utils import get_bin bin = get_bin(item_code, warehouse) mismatch = False - for fld, val in qty_dict.items(): - if flt(bin.get(fld)) != flt(val): - bin.set(fld, flt(val)) + for field, value in qty_dict.items(): + if flt(bin.get(field)) != flt(value): + bin.set(field, flt(value)) mismatch = True if mismatch: From 27f0d13bdd4490429453de3024451f466bc3227d Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Fri, 6 Mar 2020 20:55:02 +0530 Subject: [PATCH 243/929] feat: add status filter in issue web form --- erpnext/support/web_form/issues/issues.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/web_form/issues/issues.json b/erpnext/support/web_form/issues/issues.json index 652114f7382..0f15e4737fd 100644 --- a/erpnext/support/web_form/issues/issues.json +++ b/erpnext/support/web_form/issues/issues.json @@ -18,7 +18,7 @@ "is_standard": 1, "login_required": 1, "max_attachment_size": 0, - "modified": "2019-06-27 22:58:49.609672", + "modified": "2020-03-06 05:24:05.749664", "modified_by": "Administrator", "module": "Support", "name": "issues", @@ -58,7 +58,7 @@ "options": "Open\nReplied\nHold\nClosed", "read_only": 1, "reqd": 0, - "show_in_filter": 0 + "show_in_filter": 1 }, { "allow_read_on_all_link_options": 0, From 0b8191d56644bc48fe092b804702a9e59a509a0f Mon Sep 17 00:00:00 2001 From: Poranut Chollavorn Date: Thu, 26 Mar 2020 13:46:36 +0000 Subject: [PATCH 244/929] fix(pos): fix pos display item instock --- .../page/point_of_sale/point_of_sale.py | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index a9d2be5fffc..bef2c53d6d4 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -64,30 +64,40 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p for d in item_prices_data: item_prices[d.item_code] = d - + # prepare filter for bin query + bin_filters = {'item_code': ['in', items]} + if warehouse: + bin_filters['warehouse'] = warehouse if display_items_in_stock: - filters = {'actual_qty': [">", 0], 'item_code': ['in', items]} + bin_filters['actual_qty'] = [">", 0] - if warehouse: - filters['warehouse'] = warehouse + # query item bin + bin_data = frappe.get_all( + 'Bin', fields=['item_code', 'sum(actual_qty) as actual_qty'], + filters=bin_filters, group_by='item_code' + ) - bin_data = frappe._dict( - frappe.get_all("Bin", fields = ["item_code", "sum(actual_qty) as actual_qty"], - filters = filters, group_by = "item_code") - ) + # convert list of dict into dict as {item_code: actual_qty} + bin_dict = {} + for b in bin_data: + bin_dict[b.get('item_code')] = b.get('actual_qty') for item in items_data: - row = {} + item_code = item.item_code + item_price = item_prices.get(item_code) or {} + item_stock_qty = bin_dict.get(item_code) - row.update(item) - item_price = item_prices.get(item.item_code) or {} - row.update({ - 'price_list_rate': item_price.get('price_list_rate'), - 'currency': item_price.get('currency'), - 'actual_qty': bin_data.get('actual_qty') - }) - - result.append(row) + if display_items_in_stock and not item_stock_qty: + pass + else: + row = {} + row.update(item) + row.update({ + 'price_list_rate': item_price.get('price_list_rate'), + 'currency': item_price.get('currency'), + 'actual_qty': item_stock_qty, + }) + result.append(row) res = { 'items': result From d90f6581633df6e589c4dbb6d39a63b089f5f5ed Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 27 Mar 2020 12:54:01 +0530 Subject: [PATCH 245/929] fix: item not showing in popup while making batch --- erpnext/public/js/controllers/transaction.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 146895097c5..6c580d046f5 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -165,6 +165,16 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ return (doc.rule_applied) ? "green" : "red"; }); } + + let batch_no_field = this.frm.get_docfield("items", "batch_no"); + if (batch_no_field) { + batch_no_field.get_route_options_for_new_doc = function(row) { + return { + "item": row.doc.item_code + } + }; + } + }, onload: function() { var me = this; From b03d1327a57a9d0afbdf957bcb4dc98caf4f5127 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 27 Mar 2020 19:24:17 +0530 Subject: [PATCH 246/929] fix: Add item defaults based on global settings only if default company and warehouse is mentioned --- erpnext/stock/doctype/item/item.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index cb0eb406df6..24896f09da1 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -741,14 +741,12 @@ class Item(WebsiteGenerator): defaults = frappe.defaults.get_defaults() or {} # To check default warehouse is belong to the default company - if defaults.get("default_warehouse") and frappe.db.exists("Warehouse", + if defaults.get("default_warehouse") and defaults.company and frappe.db.exists("Warehouse", {'name': defaults.default_warehouse, 'company': defaults.company}): - warehouse = defaults.default_warehouse - - self.append("item_defaults", { - "company": defaults.get("company"), - "default_warehouse": warehouse - }) + self.append("item_defaults", { + "company": defaults.get("company"), + "default_warehouse": defaults.default_warehouse + }) def update_variants(self): if self.flags.dont_update_variants or \ From 2313d2d80a54d1a71141d677a7efb04a747acab4 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 27 Mar 2020 19:15:59 +0530 Subject: [PATCH 247/929] fix: total currency formatting --- .../accounts_receivable.html | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html index 791f3f8008b..bb0d0a132a5 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html @@ -218,15 +218,15 @@

    {%= __("Total") %} - {%= format_currency(data[i]["invoiced"], data[i]["currency"] ) %} - {%= format_currency(data[i]["paid"], data[i]["currency"]) %}{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} {%= format_currency(data[i]["credit_note"], data[0]["currency"]) %} - {%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}{%= data[i]["future_ref"] %}{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}{%= format_currency(data[i]["future_amount"], data[0]["currency"]) %}{%= format_currency(data[i]["remaining_balance"], data[0]["currency"]) %}{%= __("Total") %}{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}{%= format_currency(data[i]["paid"], data[i]["currency"]) %}{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}{%= format_currency(data[i]["invoiced"], data[0]["currency"]) %}{%= format_currency(data[i]["paid"], data[0]["currency"]) %}{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %}{%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}
    (a) {{__("Outward taxable supplies(other than zero rated, nil rated and exempted")}}(a) {{__("Outward taxable supplies(other than zero rated, nil rated and exempted)")}} {{ flt(data.sup_details.osup_det.txval, 2) }} {{ flt(data.sup_details.osup_det.iamt, 2) }} {{ flt(data.sup_details.osup_det.camt, 2) }}
    - {{ d.description }} - - {{ d.get_formatted("base_tax_amount") }} -
    + {{ d.description }} + + {{ d.get_formatted("base_tax_amount") }} +
    {{ item }} - {{ frappe.utils.fmt_money(itemised_taxable_amount.get(item, 0), None, currency) }} + {% if doc.get('is_return') %} + {{ frappe.utils.fmt_money((itemised_taxable_amount.get(item, 0))|abs, None, doc.currency) }} + {% else %} + {{ frappe.utils.fmt_money(itemised_taxable_amount.get(item, 0), None, doc.currency) }} + {% endif %}
    - - - - - + + + + + + @@ -17,6 +18,7 @@ + From c9014b1ba9445538e24e2667a0bb2ab6f4d4d265 Mon Sep 17 00:00:00 2001 From: Andrew McLeod Date: Tue, 26 May 2020 13:39:00 +0100 Subject: [PATCH 567/929] fix: website_list_for_contact, fix changed frappe.db.exist keyword argument (#21827) --- erpnext/controllers/website_list_for_contact.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py index ed379389d7e..ecf041efd17 100644 --- a/erpnext/controllers/website_list_for_contact.py +++ b/erpnext/controllers/website_list_for_contact.py @@ -155,7 +155,7 @@ def has_website_permission(doc, ptype, user, verbose=False): return frappe.db.exists(doctype, get_customer_filter(doc, customers)) elif suppliers: fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier' - return frappe.db.exists(doctype, filters={ + return frappe.db.exists(doctype, { 'name': doc.name, fieldname: ["in", suppliers] }) From 61257d829ec3f263c2edb6b460fd810f25672ebc Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 26 May 2020 18:11:39 +0530 Subject: [PATCH 568/929] fix: throw error if no serial numbers are found in Pick List (#21914) Co-authored-by: Marica --- erpnext/stock/doctype/pick_list/pick_list.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index c79025c1fb7..29c6c2ccbaa 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -24,6 +24,9 @@ class PickList(Document): for item in self.locations: if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'): continue + if not item.serial_no: + frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}".format( + frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse)))) if len(item.serial_no.split('\n')) == item.picked_qty: continue frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity') From f912d780235801d4db315836a3e9e0ef439852d6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 26 May 2020 18:21:31 +0530 Subject: [PATCH 569/929] fix: check for warehouse in the woocommerce settings (#21925) (#21936) (cherry picked from commit ddc170521f896567a1e2d875628432a6ab0c0abe) Co-authored-by: Mangesh-Khairnar --- .../erpnext_integrations/connectors/woocommerce_connection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 44f87e0462e..1b0c9f60b6e 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -182,7 +182,8 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr') default_warehouse = _("Stores - {0}", sys_lang).format(company_abbr) - if not frappe.db.exists("Warehouse", default_warehouse): + if not frappe.db.exists("Warehouse", default_warehouse) \ + and not woocommerce_settings.warehouse: frappe.throw(_("Please set Warehouse in Woocommerce Settings")) for item in order.get("line_items"): From 3c0e22c98ccfdf48f6c9025647f252fd78305c82 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 26 May 2020 18:37:25 +0530 Subject: [PATCH 570/929] fix: shopify error message on failure of sales order creation (#21924) (#21937) (cherry picked from commit 7402451b9695f97937d534ae649c4029491cd36a) Co-authored-by: Mangesh-Khairnar --- erpnext/erpnext_integrations/connectors/shopify_connection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index 3be08a2757a..3d92d55faaf 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -95,10 +95,10 @@ def create_sales_order(shopify_order, shopify_settings, company=None): items = get_order_items(shopify_order.get("line_items"), shopify_settings) if not items: - message = 'Following items are exists in order but relevant record not found in Product master' + message = 'Following items exists in the shopify order but relevant records were not found in the shopify Product master' message += "\n" + ", ".join(product_not_exists) - make_shopify_log(status="Error", exception=e, rollback=True) + make_shopify_log(status="Error", exception=message, rollback=True) return '' From 9720406086e7943df7b8312b9c428154cf25bc6a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 26 May 2020 19:23:45 +0530 Subject: [PATCH 571/929] fix: Do not add filters in report on accounting dimension creation if it already exists --- .../accounts_payable/accounts_payable.js | 9 +-------- .../accounts_payable_summary.js | 9 +-------- .../accounts_receivable/accounts_receivable.js | 9 +-------- .../accounts_receivable_summary.js | 9 +-------- .../report/balance_sheet/balance_sheet.js | 2 ++ erpnext/accounts/report/cash_flow/cash_flow.js | 2 ++ .../report/general_ledger/general_ledger.js | 9 +-------- .../profit_and_loss_statement.js | 2 ++ .../purchase_register/purchase_register.js | 9 +-------- .../report/sales_register/sales_register.js | 9 +-------- .../report/trial_balance/trial_balance.js | 9 +-------- erpnext/public/js/financial_statements.js | 11 +---------- erpnext/public/js/utils.js | 18 ++++++++++++++++++ 13 files changed, 33 insertions(+), 74 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index df700ec9d3c..2d2c95e79e6 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -135,12 +135,5 @@ frappe.query_reports["Accounts Payable"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Payable', 9); 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 4a9f1b0dc44..6fc0427da15 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -104,12 +104,5 @@ frappe.query_reports["Accounts Payable Summary"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Payable Summary', 9); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 5d0154f597e..a49cc321495 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -199,12 +199,5 @@ frappe.query_reports["Accounts Receivable"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Receivable', 9); 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 d54824b6855..d6f318b0eda 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -122,11 +122,4 @@ frappe.query_reports["Accounts Receivable Summary"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Receivable Summary', 9); diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index c4c24c0bab3..4a4ad4d71cb 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -4,6 +4,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Balance Sheet', 10); + frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "accumulated_values", "label": __("Accumulated Values"), diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index 89244c3781a..4e03b886a46 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -5,6 +5,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Cash Flow"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Cash Flow', 10); + // 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. diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index ac49d373d47..88ba006d527 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -160,12 +160,5 @@ frappe.query_reports["General Ledger"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('General Ledger', 15) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index baa0bda7005..568a7900216 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -6,6 +6,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Profit and Loss Statement"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Profit and Loss Statement', 10); + frappe.query_reports["Profit and Loss Statement"]["filters"].push( { "fieldname": "project", diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js index b2b95b2b81b..f34ea571639 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.js +++ b/erpnext/accounts/report/purchase_register/purchase_register.js @@ -56,11 +56,4 @@ frappe.query_reports["Purchase Register"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); \ No newline at end of file +erpnext.utils.add_dimensions('Purchase Register', 7); \ No newline at end of file diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js index 9dee656d4a9..85bbceab827 100644 --- a/erpnext/accounts/report/sales_register/sales_register.js +++ b/erpnext/accounts/report/sales_register/sales_register.js @@ -68,12 +68,5 @@ frappe.query_reports["Sales Register"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Sales Register', 7); diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 07752e1e626..9c0854c5d3a 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -102,14 +102,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "initial_depth": 3 } - erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); - }); + erpnext.utils.add_dimensions('Trial Balance', 6); }); diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index dead309a5d0..d5f83d60298 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -62,7 +62,7 @@ erpnext.financial_statements = { } }; -function get_filters(){ +function get_filters() { let filters = [ { "fieldname":"company", @@ -129,15 +129,6 @@ function get_filters(){ } ] - erpnext.dimension_filters.forEach((dimension) => { - filters.push({ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); - }); - return filters; } diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 224f4f5525d..cf9e83436ec 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -145,6 +145,7 @@ $.extend(erpnext.utils, { }, get_party_name: function(party_type) { + console.log("********"); var dict = {'Customer': 'customer_name', 'Supplier': 'supplier_name', 'Employee': 'employee_name', 'Member': 'member_name'}; return dict[party_type]; @@ -191,6 +192,23 @@ $.extend(erpnext.utils, { }) }, + add_dimensions: function(report_name, index) { + let filters = frappe.query_reports[report_name].filters; + + erpnext.dimension_filters.forEach((dimension) => { + let found = filters.some(el => el.fieldname === dimension['fieldname']); + + if (!found) { + filters.splice(index, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + } + }); + }, + make_subscription: function(doctype, docname) { frappe.call({ method: "frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat", From bb2bcc8a30609a441ea794876098634bda66a810 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 26 May 2020 19:26:47 +0530 Subject: [PATCH 572/929] fix: Remove console statement --- erpnext/public/js/utils.js | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index cf9e83436ec..fb7471150cc 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -145,7 +145,6 @@ $.extend(erpnext.utils, { }, get_party_name: function(party_type) { - console.log("********"); var dict = {'Customer': 'customer_name', 'Supplier': 'supplier_name', 'Employee': 'employee_name', 'Member': 'member_name'}; return dict[party_type]; From aee9a61e0d7ca856fa14ca57c8d07d91cb6e23a4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 27 May 2020 12:46:18 +0530 Subject: [PATCH 573/929] fix: Post Dated unallocated amount not considered in Advance Amount in AR/AP summary (#21838) * fix: Post Dated unallocateed amount not considered in Advance Amount in AR/AP summary report * fix: Add future payment filter in AR/AP summary * fix: Show unallocated future payments only till current creation date * fix: Remove extra query * fix: Remove debug * fix: Condition --- erpnext/accounts/party.py | 8 ++++++-- .../accounts_receivable/accounts_receivable.py | 13 ++++++++++--- .../accounts_receivable_summary.js | 7 ++++++- .../accounts_receivable_summary.py | 2 +- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index b5dfbde5a04..b0b64c8b56f 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -605,10 +605,14 @@ def get_party_shipping_address(doctype, name): else: return '' -def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None): +def get_partywise_advanced_payment_amount(party_type, posting_date = None, future_payment=0, company=None): cond = "1=1" if posting_date: - cond = "posting_date <= '{0}'".format(posting_date) + if future_payment: + cond = "posting_date <= '{0}' OR DATE(creation) <= '{0}' """.format(posting_date) + else: + cond = "posting_date <= '{0}'".format(posting_date) + if company: cond += "and company = '{0}'".format(company) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index e9c286fcf0d..0fe62143e2d 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -559,6 +559,14 @@ class ReceivablePayableReport(object): conditions, values = self.prepare_conditions() order_by = self.get_order_by_condition() + if self.filters.show_future_payments: + values.insert(2, self.filters.report_date) + + date_condition = """AND (posting_date <= %s + OR (against_voucher IS NULL AND DATE(creation) <= %s))""" + else: + date_condition = "AND posting_date <=%s" + if self.filters.get(scrub(self.party_type)): select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit" else: @@ -574,9 +582,8 @@ class ReceivablePayableReport(object): docstatus < 2 and party_type=%s and (party is not null and party != '') - and posting_date <= %s - {1} {2}""" - .format(select_fields, conditions, order_by), values, as_dict=True) + {1} {2} {3}""" + .format(select_fields, date_condition, conditions, order_by), values, as_dict=True) def get_sales_invoices_or_customers_based_on_sales_person(self): if self.filters.get("sales_person"): 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 d6f318b0eda..ed46cf2d9f1 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -111,7 +111,12 @@ frappe.query_reports["Accounts Receivable Summary"] = { "fieldname":"based_on_payment_terms", "label": __("Based On Payment Terms"), "fieldtype": "Check", - } + }, + { + "fieldname":"show_future_payments", + "label": __("Show Future Payments"), + "fieldtype": "Check", + }, ], onload: function(report) { diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py index aa6b42e89d0..657b3e8f204 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -33,7 +33,7 @@ class AccountsReceivableSummary(ReceivablePayableReport): self.get_party_total(args) party_advance_amount = get_partywise_advanced_payment_amount(self.party_type, - self.filters.report_date, self.filters.company) or {} + self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {} for party, party_dict in iteritems(self.party_total): if party_dict.outstanding == 0: From b4a3e5f70be8b7c12f2bdd41534df95d975d2fc2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 27 May 2020 20:30:41 +0530 Subject: [PATCH 574/929] fix: Total for ageing column 121-Above (#21973) --- .../accounts/report/accounts_receivable/accounts_receivable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 0fe62143e2d..4015b056c58 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -534,7 +534,7 @@ class ReceivablePayableReport(object): def get_ageing_data(self, entry_date, row): # [0-30, 30-60, 60-90, 90-120, 120-above] - row.range1 = row.range2 = row.range3 = row.range4 = range5 = 0.0 + row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0 if not (self.age_as_on and entry_date): return From d9e6900e0759f86f668f8879fa62e6b2e199545b Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 27 May 2020 21:53:01 +0530 Subject: [PATCH 575/929] fix: Added permission via regional setup and patch --- erpnext/patches.txt | 1 + ...ian_import_supplier_invoice_permissions.py | 12 +++++ .../import_supplier_invoice.json | 44 +------------------ erpnext/regional/italy/setup.py | 20 +++++++++ 4 files changed, 34 insertions(+), 43 deletions(-) create mode 100644 erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 74f729678d0..10c875888e1 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -667,3 +667,4 @@ execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom +erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions \ No newline at end of file diff --git a/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py new file mode 100644 index 00000000000..a6011c4dace --- /dev/null +++ b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py @@ -0,0 +1,12 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.regional.italy.setup import add_permissions + +def execute(): + countries = frappe.get_all("Company", fields="country") + countries = [country["country"] for country in countries] + if "Italy" in countries: + add_permissions() \ No newline at end of file diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json index 6b45841b465..c1680c4b492 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json @@ -96,49 +96,7 @@ "module": "Regional", "name": "Import Supplier Invoice", "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "share": 1, - "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase User" - }, - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "share": 1, - "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Auditor" - }, - { - "permlevel": 1, - "read": 1, - "role": "Accounts Manager", - "write": 1 - } - ], + "permissions": [], "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 diff --git a/erpnext/regional/italy/setup.py b/erpnext/regional/italy/setup.py index 2d0ad66b0a0..6ab73413df2 100644 --- a/erpnext/regional/italy/setup.py +++ b/erpnext/regional/italy/setup.py @@ -7,11 +7,13 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_fields +from frappe.permissions import add_permission, update_permission_property from erpnext.regional.italy import fiscal_regimes, tax_exemption_reasons, mode_of_payment_codes, vat_collectability_options def setup(company=None, patch=True): make_custom_fields() setup_report() + add_permissions() def make_custom_fields(update=True): invoice_item_fields = [ @@ -200,3 +202,21 @@ def setup_report(): dict(role='Accounts Manager') ] )).insert() + +def add_permissions(): + doctype = 'Import Supplier Invoice' + add_permission(doctype, 'All', 0) + + for role in ('Accounts Manager', 'Accounts User','Purchase User', 'Auditor'): + add_permission(doctype, role, 0) + update_permission_property(doctype, role, 0, 'print', 1) + update_permission_property(doctype, role, 0, 'report', 1) + + if role in ('Accounts Manager', 'Accounts User'): + update_permission_property(doctype, role, 0, 'write', 1) + update_permission_property(doctype, role, 0, 'create', 1) + + update_permission_property(doctype, 'Accounts Manager', 0, 'delete', 1) + add_permission(doctype, 'Accounts Manager', 1) + update_permission_property(doctype, 'Accounts Manager', 1, 'write', 1) + update_permission_property(doctype, 'Accounts Manager', 1, 'create', 1) \ No newline at end of file From a23f92900547412736db7eec4797cc7db5880dcb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 27 May 2020 22:44:50 +0530 Subject: [PATCH 576/929] fix: TDS computation summary report --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index dd6b4fdc603..30bac5efa68 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -180,7 +180,7 @@ def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=No if company: condition += "and company =%s" % (company) if from_date and to_date: - condition += "and posting_date between %s and %s" % (company, from_date, to_date) + condition += "and posting_date between %s and %s" % (from_date, to_date) ## Appending the same supplier again if length of suppliers list is 1 ## since tuple of single element list contains None, For example ('Test Supplier 1', ) From 64078da47b680027a745ef03f41f8b430f9289d0 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 28 May 2020 11:10:32 +0530 Subject: [PATCH 577/929] fix: change description for field (#21995) (#21996) grammatical fixes for description of Default Customer (cherry picked from commit f7beeff2d8633538372a9bf992d9249869428d1f) Co-authored-by: Michelle Alva <50285544+michellealva@users.noreply.github.com> --- .../doctype/shopify_settings/shopify_settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json index 8f1b746e5b6..d1e5f40d6c1 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json @@ -133,7 +133,7 @@ "label": "Customer Settings" }, { - "description": "If Shopify not contains a customer in Order, then while syncing Orders, the system will consider default customer for order", + "description": "If Shopify does not have a customer in the order, then while syncing the orders, the system will consider the default customer for the order", "fieldname": "default_customer", "fieldtype": "Link", "label": "Default Customer", @@ -277,4 +277,4 @@ ], "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} From d65000a3614ca281b5f7983f8f6195e5f2bdfe58 Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Thu, 28 May 2020 12:52:58 +0530 Subject: [PATCH 578/929] fix: change modified time changed modified time to sync the changes (cherry picked from commit 8653419c4cd5241a09f9bb34bdd269ad8fcdc0d1) --- .../doctype/shopify_settings/shopify_settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json index d1e5f40d6c1..5339c99155b 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json @@ -258,7 +258,7 @@ } ], "issingle": 1, - "modified": "2019-09-13 12:32:11.384757", + "modified": "2020-05-28 12:32:11.384757", "modified_by": "umair@erpnext.com", "module": "ERPNext Integrations", "name": "Shopify Settings", From 0fcb79f3610f214e0b584a1e52ad60f4e68e3f17 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 29 May 2020 10:17:18 +0530 Subject: [PATCH 579/929] fix: make transaction date of the oldest transaction as the last integration date (#22017) * fix: make transaction date of the oldest transaction as the last integration date * fix: only save end date when transactions are returned --- .../doctype/plaid_settings/plaid_settings.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index c175eaaa251..81fb9843f61 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -124,10 +124,12 @@ def add_account_subtype(account_subtype): @frappe.whitelist() def sync_transactions(bank, bank_account): + ''' Sync transactions based on the last integration date as the start date, after sync is completed + add the transaction date of the oldest transaction as the last integration date ''' - last_sync_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") - if last_sync_date: - start_date = formatdate(last_sync_date, "YYYY-MM-dd") + last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") + if last_transaction_date: + start_date = formatdate(last_transaction_date, "YYYY-MM-dd") else: start_date = formatdate(add_months(today(), -12), "YYYY-MM-dd") end_date = formatdate(today(), "YYYY-MM-dd") @@ -139,12 +141,14 @@ def sync_transactions(bank, bank_account): for transaction in reversed(transactions): result += new_bank_transaction(transaction) - frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( - len(result), bank_account, start_date, end_date)) + if result: + last_transaction_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date') - frappe.db.set_value("Bank Account", bank_account, "last_integration_date", getdate(end_date)) + frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( + len(result), bank_account, start_date, end_date)) + + frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date) - return result except Exception: frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error")) From 840225d47b1d8f25a7843110975e2719114f8f08 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 5 May 2020 16:10:47 +0530 Subject: [PATCH 580/929] fix: cannot make payment entry against shareholder (#21597) (cherry picked from commit d46696fa034e41a33008bdb232529d4162f5956a) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index ab7aa15e3dd..eae801368ca 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -227,6 +227,8 @@ class PaymentEntry(AccountsController): valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry") elif self.party_type == "Employee": valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance") + elif self.party_type == "Shareholder": + valid_reference_doctypes = ("Journal Entry") for d in self.get("references"): if not d.allocated_amount: From 5b695d6e9feea51bf9100f97907122f0c868131b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 29 May 2020 21:26:16 +0530 Subject: [PATCH 581/929] fix: transaction date not found in sales invoice (bp #22001) (#22029) * fix: transaction date not found in sales invoice (cherry picked from commit 603cc3d05e8729f9133ef30b228db53eb8377e15) * chore: tests (cherry picked from commit dd39ba00140a348af397c899d93576a0f8d6ea6c) Co-authored-by: Saqib Ansari --- .../doctype/pricing_rule/test_pricing_rule.py | 44 +++++++++++++++++++ .../accounts/doctype/pricing_rule/utils.py | 3 +- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 2da71dfd0e0..2bf0b725635 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -385,6 +385,50 @@ class TestPricingRule(unittest.TestCase): so.load_from_db() self.assertEqual(so.items[1].is_free_item, 1) self.assertEqual(so.items[1].item_code, "_Test Item 2") + + def test_cumulative_pricing_rule(self): + frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule') + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Cumulative Pricing Rule", + "apply_on": "Item Code", + "currency": "USD", + "items": [{ + "item_code": "_Test Item", + }], + "is_cumulative": 1, + "selling": 1, + "applicable_for": "Customer", + "customer": "_Test Customer", + "rate_or_discount": "Discount Percentage", + "rate": 0, + "min_amt": 0, + "max_amt": 10000, + "discount_percentage": 17.5, + "price_or_product_discount": "Price", + "company": "_Test Company", + "valid_from": frappe.utils.nowdate(), + "valid_upto": frappe.utils.nowdate() + } + frappe.get_doc(test_record.copy()).insert() + + args = frappe._dict({ + "item_code": "_Test Item", + "company": "_Test Company", + "price_list": "_Test Price List", + "currency": "_Test Currency", + "doctype": "Sales Invoice", + "conversion_rate": 1, + "price_list_currency": "_Test Currency", + "plc_conversion_rate": 1, + "order_type": "Sales", + "customer": "_Test Customer", + "name": None, + "transaction_date": frappe.utils.nowdate() + }) + details = get_item_details(args) + + self.assertTrue(details) def make_pricing_rule(**args): args = frappe._dict(args) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index cb05481df5a..53115f92d01 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -366,8 +366,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]): sum_qty, sum_amt = [0, 0] doctype = doc.get('parenttype') or doc.doctype - date_field = ('transaction_date' - if doc.get('transaction_date') else 'posting_date') + date_field = 'transaction_date' if frappe.get_meta(doctype).has_field('transaction_date') else 'posting_date' child_doctype = '{0} Item'.format(doctype) apply_on = frappe.scrub(pr_doc.get('apply_on')) From 592965a4d9ed9069a587a41f1f2034dfe5e27708 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 29 May 2020 21:26:29 +0530 Subject: [PATCH 582/929] fix: disposed asset creates inconsistencies in asset depr report (#22021) (#22030) (cherry picked from commit 3c460364b5345ae823e150d532ee625b64475971) Co-authored-by: Saqib --- .../asset_depreciations_and_balances.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 80bccafd28e..5001ad9f12f 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -93,7 +93,7 @@ def get_assets(filters): sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period from (SELECT a.asset_category, - ifnull(sum(case when ds.schedule_date < %(from_date)s then + ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then ds.depreciation_amount else 0 @@ -115,9 +115,7 @@ def get_assets(filters): group by a.asset_category union SELECT a.asset_category, - ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 - and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) - then + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then 0 else a.opening_accumulated_depreciation From 0407f018ddfece439932b799a606ef20248474d2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 29 May 2020 21:26:40 +0530 Subject: [PATCH 583/929] fix: cannot assign same task to other asset maintenance (#22024) (#22032) (cherry picked from commit cd8b5d1e4ce8ae809b6ad36b0ccdb89ee75ca4ce) Co-authored-by: Saqib --- .../asset_maintenance/asset_maintenance.json | 643 ++---------- .../asset_maintenance/asset_maintenance.py | 6 +- .../asset_maintenance_log.json | 967 ++++-------------- .../asset_maintenance_task.py | 3 +- 4 files changed, 295 insertions(+), 1324 deletions(-) diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json index efb2de3f266..c0c2566fe23 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json @@ -1,559 +1,140 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:asset_name", - "beta": 0, - "creation": "2017-10-19 16:50:22.879545", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "field:asset_name", + "creation": "2017-10-19 16:50:22.879545", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_name", + "asset_category", + "company", + "column_break_3", + "item_code", + "item_name", + "section_break_6", + "maintenance_team", + "column_break_9", + "maintenance_manager", + "maintenance_manager_name", + "section_break_8", + "asset_maintenance_tasks" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_name", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Asset Name", - "length": 0, - "no_copy": 0, - "options": "Asset", - "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 - }, + "fieldname": "asset_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset Name", + "options": "Asset", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.asset_category", - "fieldname": "asset_category", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Asset Category", - "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 - }, + "fieldname": "asset_category", + "fieldtype": "Read Only", + "label": "Asset Category" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.item_code", - "fieldname": "item_code", - "fieldtype": "Read Only", - "hidden": 0, - "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 Code", - "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 - }, + "fieldname": "item_code", + "fieldtype": "Read Only", + "label": "Item Code" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.item_name", - "fieldname": "item_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "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 - }, + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 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, - "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 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_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": "Select Serial No", - "length": 0, - "no_copy": 0, - "options": "Serial No", - "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_6", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "serial_no", - "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": "Serial No", - "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 - }, + "fieldname": "maintenance_team", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Maintenance Team", + "options": "Asset Maintenance Team", + "reqd": 1 + }, { - "allow_bulk_edit": 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 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_team", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Maintenance Team", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance Team", - "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "maintenance_team.maintenance_manager", - "fieldname": "maintenance_manager", - "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": "Maintenance Manager", - "length": 0, - "no_copy": 0, - "options": "", - "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": "maintenance_manager", + "fieldtype": "Data", + "hidden": 1, + "label": "Maintenance Manager", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "maintenance_team.maintenance_manager_name", - "fieldname": "maintenance_manager_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintenance Manager Name", - "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 - }, + "fieldname": "maintenance_manager_name", + "fieldtype": "Read Only", + "label": "Maintenance Manager Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tasks", - "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_8", + "fieldtype": "Section Break", + "label": "Tasks" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_maintenance_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": "Maintenance Tasks", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance 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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "asset_maintenance_tasks", + "fieldtype": "Table", + "label": "Maintenance Tasks", + "options": "Asset Maintenance Task", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-05-22 17:20:54.711885", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-05-28 20:28:32.993823", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Quality Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, "write": 1 - }, + }, { - "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": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index ecb55dde9ae..ba63dd661d7 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -39,7 +39,7 @@ class AssetMaintenance(Document): @frappe.whitelist() def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, next_due_date): - team_member = frappe.get_doc('User', assign_to_member).email + team_member = frappe.db.get_value('User', assign_to_member, "email") args = { 'doctype' : 'Asset Maintenance', 'assign_to' : team_member, @@ -78,7 +78,7 @@ def calculate_next_due_date(periodicity, start_date = None, end_date = None, las def update_maintenance_log(asset_maintenance, item_code, item_name, task): asset_maintenance_log = frappe.get_value("Asset Maintenance Log", {"asset_maintenance": asset_maintenance, - "task": task.maintenance_task, "maintenance_status": ('in',['Planned','Overdue'])}) + "task": task.name, "maintenance_status": ('in',['Planned','Overdue'])}) if not asset_maintenance_log: asset_maintenance_log = frappe.get_doc({ @@ -87,7 +87,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task): "asset_name": asset_maintenance, "item_code": item_code, "item_name": item_name, - "task": task.maintenance_task, + "task": task.name, "has_certificate": task.certificate_required, "description": task.description, "assign_to_name": task.assign_to_name, diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json index 42aecdf44ac..7395bec1e62 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json @@ -1,819 +1,210 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2017-10-23 16:58:44.424309", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "naming_series:", + "creation": "2017-10-23 16:58:44.424309", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_maintenance", + "naming_series", + "asset_name", + "column_break_2", + "item_code", + "item_name", + "section_break_5", + "task", + "task_name", + "maintenance_type", + "periodicity", + "assign_to_name", + "column_break_6", + "due_date", + "completion_date", + "maintenance_status", + "section_break_12", + "has_certificate", + "certificate_attachement", + "section_break_6", + "description", + "column_break_9", + "actions_performed", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_maintenance", - "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": "Asset Maintenance", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance", - "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": "asset_maintenance", + "fieldtype": "Link", + "label": "Asset Maintenance", + "options": "Asset Maintenance" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 0, - "options": "ACC-AML-.YYYY.-", - "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 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "ACC-AML-.YYYY.-", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.asset_name", - "fieldname": "asset_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Asset Name", - "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 - }, + "fetch_from": "asset_maintenance.asset_name", + "fieldname": "asset_name", + "fieldtype": "Read Only", + "label": "Asset Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.item_code", - "fieldname": "item_code", - "fieldtype": "Read Only", - "hidden": 0, - "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 Code", - "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 - }, + "fetch_from": "asset_maintenance.item_code", + "fieldname": "item_code", + "fieldtype": "Read Only", + "label": "Item Code" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.item_name", - "fieldname": "item_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "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 - }, + "fetch_from": "asset_maintenance.item_name", + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "task", - "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": "Task", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance 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 - }, + "fieldname": "task", + "fieldtype": "Link", + "label": "Task", + "options": "Asset Maintenance Task" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.maintenance_type", - "fieldname": "maintenance_type", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintenance Type", - "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 - }, + "fetch_from": "task.maintenance_type", + "fieldname": "maintenance_type", + "fieldtype": "Read Only", + "label": "Maintenance Type" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.periodicity", - "fieldname": "periodicity", - "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": "Periodicity", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "fetch_from": "task.periodicity", + "fieldname": "periodicity", + "fieldtype": "Data", + "label": "Periodicity", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.assign_to_name", - "fieldname": "assign_to_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Assign To", - "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 - }, + "fetch_from": "task.assign_to_name", + "fieldname": "assign_to_name", + "fieldtype": "Read Only", + "label": "Assign To" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.next_due_date", - "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": 1, - "in_standard_filter": 0, - "label": "Due Date", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "fetch_from": "task.next_due_date", + "fieldname": "due_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Due Date", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "completion_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": "Completion 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 - }, + "fieldname": "completion_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Completion Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_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": "Maintenance Status", - "length": 0, - "no_copy": 0, - "options": "Planned\nCompleted\nCancelled\nOverdue", - "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 - }, + "fieldname": "maintenance_status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Maintenance Status", + "options": "Planned\nCompleted\nCancelled\nOverdue", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_12", - "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_12", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.certificate_required", - "fieldname": "has_certificate", - "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": "Has Certificate ", - "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 - }, + "default": "0", + "fetch_from": "task.certificate_required", + "fieldname": "has_certificate", + "fieldtype": "Check", + "label": "Has Certificate " + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.has_certificate", - "fieldname": "certificate_attachement", - "fieldtype": "Attach", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Certificate", - "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 - }, + "depends_on": "eval:doc.has_certificate", + "fieldname": "certificate_attachement", + "fieldtype": "Attach", + "label": "Certificate" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.description", - "fieldname": "description", - "fieldtype": "Read Only", - "hidden": 0, - "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, - "options": "", - "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 - }, + "fetch_from": "task.description", + "fieldname": "description", + "fieldtype": "Read Only", + "label": "Description", + "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_9", - "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": "column_break_9", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "actions_performed", - "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": "Actions performed", - "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_on_submit": 1, + "fieldname": "actions_performed", + "fieldtype": "Text Editor", + "label": "Actions performed" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Asset Maintenance Log", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Asset Maintenance Log", + "print_hide": 1, + "read_only": 1 + }, + { + "fetch_from": "task.maintenance_task", + "fieldname": "task_name", + "fieldtype": "Data", + "in_preview": 1, + "label": "Task Name", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:51.457835", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance Log", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-05-28 20:51:48.238397", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance Log", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py index d98cc310b3b..2a5666d5064 100644 --- a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py +++ b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py @@ -7,5 +7,4 @@ import frappe from frappe.model.document import Document class AssetMaintenanceTask(Document): - def autoname(self): - self.name = self.maintenance_task + pass From f214789e4b868e64358953e2b9806380aeddfd2c Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 29 May 2020 21:27:31 +0530 Subject: [PATCH 584/929] fix: routing operations table is blank on pull of operations in BOM (#22040) --- erpnext/manufacturing/doctype/bom/bom.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index d11077270aa..4f40fb71fc1 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -102,7 +102,13 @@ class BOM(WebsiteGenerator): self.set("operations", []) for d in frappe.get_all("BOM Operation", fields = ["*"], filters = {'parenttype': 'Routing', 'parent': self.routing}): - child = self.append('operations', d) + child = self.append('operations', { + "operation": d.operation, + "workstation": d.workstation, + "description": d.description, + "time_in_mins": d.time_in_mins, + "batch_size": d.batch_size + }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) def validate_rm_item(self, item): From e7e3af1684911b244c93eb3eb26f06a941ecd925 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 11 May 2020 11:44:45 +0530 Subject: [PATCH 585/929] fix: '<' not supported between instances of 'str' and 'int' --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 66145c650be..e48a2211e32 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -178,7 +178,7 @@ class StockEntry(StockController): stock_items = self.get_stock_items() serialized_items = self.get_serialized_items() for item in self.get("items"): - if item.qty and item.qty < 0: + if flt(item.qty) and flt(item.qty) < 0: frappe.throw(_("Row {0}: The item {1}, quantity must be positive number") .format(item.idx, frappe.bold(item.item_code))) From ffbc8122716198ad9a100c3dd7ce6fd660b85cf2 Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Sat, 30 May 2020 15:00:56 +0530 Subject: [PATCH 586/929] fix(ewb): remove checksum validation for TRANSIN (cherry picked from commit ca46bedfcb874ac5e03d44a00b996d31db336d85) --- erpnext/regional/india/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index d22aa1eebb8..abcc953277a 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -600,8 +600,9 @@ def get_transport_details(data, doc): data.transDocDate = frappe.utils.formatdate(doc.lr_date, 'dd/mm/yyyy') if doc.gst_transporter_id: - validate_gstin_check_digit(doc.gst_transporter_id, label='GST Transporter ID') - data.transporterId = doc.gst_transporter_id + if doc.gst_transporter_id[0:2] != "88": + validate_gstin_check_digit(doc.gst_transporter_id, label='GST Transporter ID') + data.transporterId = doc.gst_transporter_id return data From 144facf2033342ce4f5606a83c74f0514c0c7329 Mon Sep 17 00:00:00 2001 From: marination Date: Sun, 31 May 2020 20:11:40 +0530 Subject: [PATCH 587/929] fix: Procurement Tracker Data Consistency --- .../procurement_tracker.py | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index 39668795cba..88a865f0f85 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils import flt def execute(filters=None): columns = get_columns(filters) @@ -54,15 +55,16 @@ def get_columns(filters): "width": 140 }, { - "label": _("Description"), - "fieldname": "description", - "fieldtype": "Data", - "width": 200 + "label": _("Item"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 150 }, { "label": _("Quantity"), "fieldname": "quantity", - "fieldtype": "Int", + "fieldtype": "Float", "width": 140 }, { @@ -118,7 +120,7 @@ def get_columns(filters): }, { "label": _("Purchase Order Amount(Company Currency)"), - "fieldname": "purchase_order_amt_usd", + "fieldname": "purchase_order_amt_in_company_currency", "fieldtype": "Float", "width": 140 }, @@ -175,17 +177,17 @@ def get_data(filters): "requesting_site": po.warehouse, "requestor": po.owner, "material_request_no": po.material_request, - "description": po.description, - "quantity": po.qty, + "item_code": po.item_code, + "quantity": flt(po.qty), "unit_of_measurement": po.stock_uom, "status": po.status, "purchase_order_date": po.transaction_date, "purchase_order": po.parent, "supplier": po.supplier, - "estimated_cost": mr_record.get('amount'), - "actual_cost": pi_records.get(po.name), - "purchase_order_amt": po.amount, - "purchase_order_amt_in_company_currency": po.base_amount, + "estimated_cost": flt(mr_record.get('amount')), + "actual_cost": flt(pi_records.get(po.name)), + "purchase_order_amt": flt(po.amount), + "purchase_order_amt_in_company_currency": flt(po.base_amount), "expected_delivery_date": po.schedule_date, "actual_delivery_date": pr_records.get(po.name) } @@ -198,9 +200,14 @@ def get_mapped_mr_details(conditions): SELECT par.transaction_date, par.per_ordered, + par.owner, child.name, child.parent, - child.amount + child.amount, + child.qty, + child.item_code, + child.uom, + par.status FROM `tabMaterial Request` par, `tabMaterial Request Item` child WHERE par.per_ordered>=0 @@ -217,7 +224,15 @@ def get_mapped_mr_details(conditions): procurement_record_details = dict( material_request_date=record.transaction_date, material_request_no=record.parent, - estimated_cost=record.amount + requestor=record.owner, + item_code=record.item_code, + estimated_cost=flt(record.amount), + quantity=flt(record.qty), + unit_of_measurement=record.uom, + status=record.status, + actual_cost=0, + purchase_order_amt=0, + purchase_order_amt_in_company_currency=0 ) procurement_record_against_mr.append(procurement_record_details) return mr_records, procurement_record_against_mr @@ -259,7 +274,7 @@ def get_po_entries(conditions): child.warehouse, child.material_request, child.material_request_item, - child.description, + child.item_code, child.stock_uom, child.qty, child.amount, From f42d656cd316b15e4a835d4b3c855cf49d73416b Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 1 Jun 2020 11:56:33 +0530 Subject: [PATCH 588/929] fix: Misleading Error message for Item Attribute. --- erpnext/controllers/item_variant.py | 15 +++++++++++---- .../doctype/item_attribute/item_attribute.py | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 29f8dd57026..50b17abbe6d 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -70,7 +70,7 @@ def validate_item_variant_attributes(item, args=None): else: attributes_list = attribute_values.get(attribute.lower(), []) - validate_item_attribute_value(attributes_list, attribute, value, item.name) + validate_item_attribute_value(attributes_list, attribute, value, item.name, from_variant=True) def validate_is_incremental(numeric_attribute, attribute, value, item): from_range = numeric_attribute.from_range @@ -93,13 +93,20 @@ def validate_is_incremental(numeric_attribute, attribute, value, item): .format(attribute, from_range, to_range, increment, item), InvalidItemAttributeValueError, title=_('Invalid Attribute')) -def validate_item_attribute_value(attributes_list, attribute, attribute_value, item): +def validate_item_attribute_value(attributes_list, attribute, attribute_value, item, from_variant=True): allow_rename_attribute_value = frappe.db.get_single_value('Item Variant Settings', 'allow_rename_attribute_value') if allow_rename_attribute_value: pass elif attribute_value not in attributes_list: - frappe.throw(_("The value {0} is already assigned to an exisiting Item {2}.").format( - attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Rename Not Allowed')) + if from_variant: + frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format( + frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value")) + else: + msg = _("The value {0} is already assigned to an exisiting Item {1}.").format( + frappe.bold(attribute_value), frappe.bold(item)) + msg += "
    " + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value")) + + frappe.throw(msg, InvalidItemAttributeValueError, title=_('Edit Not Allowed')) def get_attribute_values(item): if not frappe.flags.attribute_values: diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.py b/erpnext/stock/doctype/item_attribute/item_attribute.py index 71b998fb954..2f75bbd97c0 100644 --- a/erpnext/stock/doctype/item_attribute/item_attribute.py +++ b/erpnext/stock/doctype/item_attribute/item_attribute.py @@ -34,7 +34,7 @@ class ItemAttribute(Document): if self.numeric_values: validate_is_incremental(self, self.name, item.value, item.name) else: - validate_item_attribute_value(attributes_list, self.name, item.value, item.name) + validate_item_attribute_value(attributes_list, self.name, item.value, item.name, from_variant=False) def validate_numeric(self): if self.numeric_values: From 5c448de2ae8764280d493dac79e1ca23059b0dd0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 24 Apr 2020 04:07:14 +0530 Subject: [PATCH 589/929] feat: Add Failed error logs fix: set defaults (cherry picked from commit defeb737471efab0100df5682777572ed0c4e2ee) --- .../tally_migration/tally_migration.json | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json index dc6f093ac9d..005c8a98c6b 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json @@ -28,14 +28,17 @@ "vouchers", "accounts_section", "default_warehouse", - "round_off_account", + "default_round_off_account", "column_break_21", "default_cost_center", "day_book_section", "day_book_data", "column_break_27", "is_day_book_data_processed", - "is_day_book_data_imported" + "is_day_book_data_imported", + "import_log_section", + "failed_import_log", + "failed_import_preview" ], "fields": [ { @@ -57,6 +60,7 @@ "fieldname": "tally_creditors_account", "fieldtype": "Data", "label": "Tally Creditors Account", + "read_only_depends_on": "eval:doc.is_master_data_processed==1", "reqd": 1 }, { @@ -69,6 +73,7 @@ "fieldname": "tally_debtors_account", "fieldtype": "Data", "label": "Tally Debtors Account", + "read_only_depends_on": "eval:doc.is_master_data_processed==1", "reqd": 1 }, { @@ -92,7 +97,7 @@ "fieldname": "erpnext_company", "fieldtype": "Data", "label": "ERPNext Company", - "read_only_depends_on": "eval:doc.is_master_data_processed == 1" + "read_only_depends_on": "eval:doc.is_master_data_processed==1" }, { "fieldname": "processed_files_section", @@ -136,6 +141,7 @@ }, { "depends_on": "is_master_data_imported", + "description": "The accounts are set by the system automatically but do confirm these defaults", "fieldname": "accounts_section", "fieldtype": "Section Break", "label": "Accounts" @@ -146,12 +152,6 @@ "label": "Default Warehouse", "options": "Warehouse" }, - { - "fieldname": "round_off_account", - "fieldtype": "Link", - "label": "Round Off Account", - "options": "Account" - }, { "fieldname": "column_break_21", "fieldtype": "Column Break" @@ -212,11 +212,36 @@ "fieldname": "default_uom", "fieldtype": "Link", "label": "Default UOM", - "options": "UOM" + "options": "UOM", + "read_only_depends_on": "eval:doc.is_master_data_imported==1" + }, + { + "default": "[]", + "fieldname": "failed_import_log", + "fieldtype": "Code", + "hidden": 1, + "label": "Failed Import Log", + "options": "JSON" + }, + { + "fieldname": "failed_import_preview", + "fieldtype": "HTML", + "label": "Failed Import Log" + }, + { + "fieldname": "import_log_section", + "fieldtype": "Section Break", + "label": "Import Log" + }, + { + "fieldname": "default_round_off_account", + "fieldtype": "Link", + "label": "Default Round Off Account", + "options": "Account" } ], "links": [], - "modified": "2020-04-16 13:03:28.894919", + "modified": "2020-04-22 16:01:13.718842", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Tally Migration", From 61381963b764467a724ae8da83692b824afd38b4 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 27 Apr 2020 11:34:00 +0530 Subject: [PATCH 590/929] feat: Error logging via doc fix: Handle duplicate company and COA feat: Error logging via doc feat: fetch defaults for accounts (cherry picked from commit 2de4a5a7c884080db1ac03acf40262ca887f8913) --- .../tally_migration/tally_migration.py | 99 ++++++++++++------- 1 file changed, 65 insertions(+), 34 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 13474e19ee1..32d11681fb0 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import json import re +import sys import traceback import zipfile from decimal import Decimal @@ -15,13 +16,14 @@ from bs4 import BeautifulSoup as bs import frappe from erpnext import encode_company_abbr from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts +from erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer import unset_existing_data + from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.model.document import Document from frappe.model.naming import getseries, revert_series_if_last from frappe.utils.data import format_datetime - PRIMARY_ACCOUNT = "Primary" VOUCHER_CHUNK_SIZE = 500 @@ -65,9 +67,17 @@ class TallyMigration(Document): "attached_to_name": self.name, "content": json.dumps(value), "is_private": True - }).insert() + }) + try: + f.insert() + except frappe.DuplicateEntryError: + pass setattr(self, key, f.file_url) + def set_account_defaults(self): + self.default_cost_center, self.default_round_off_account = frappe.db.get_value("Company", self.erpnext_company, ["cost_center", "round_off_account"]) + self.default_warehouse = frappe.db.get_value("Stock Settings", "Stock Settings", "default_warehouse") + def _process_master_data(self): def get_company_name(collection): return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string.strip() @@ -84,7 +94,11 @@ class TallyMigration(Document): children, parents = get_children_and_parent_dict(accounts) group_set = [acc[1] for acc in accounts if acc[2]] children, customers, suppliers = remove_parties(parents, children, group_set) - coa = traverse({}, children, roots, roots, group_set) + + try: + coa = traverse({}, children, roots, roots, group_set) + except RecursionError: + self.log() for account in coa: coa[account]["root_type"] = root_type_map[account] @@ -242,12 +256,18 @@ class TallyMigration(Document): def create_company_and_coa(coa_file_url): coa_file = frappe.get_doc("File", {"file_url": coa_file_url}) frappe.local.flags.ignore_chart_of_accounts = True - company = frappe.get_doc({ - "doctype": "Company", - "company_name": self.erpnext_company, - "default_currency": "INR", - "enable_perpetual_inventory": 0, - }).insert() + + try: + company = frappe.get_doc({ + "doctype": "Company", + "company_name": self.erpnext_company, + "default_currency": "INR", + "enable_perpetual_inventory": 0, + }).insert() + except frappe.DuplicateEntryError: + company = frappe.get_doc("Company", self.erpnext_company) + unset_existing_data(self.erpnext_company) + frappe.local.flags.ignore_chart_of_accounts = False create_charts(company.name, custom_chart=json.loads(coa_file.get_content())) company.create_default_warehouses() @@ -256,36 +276,35 @@ class TallyMigration(Document): parties_file = frappe.get_doc("File", {"file_url": parties_file_url}) for party in json.loads(parties_file.get_content()): try: - frappe.get_doc(party).insert() + party_doc = frappe.get_doc(party) + party_doc.insert() except: - self.log(party) + self.log(party_doc) addresses_file = frappe.get_doc("File", {"file_url": addresses_file_url}) for address in json.loads(addresses_file.get_content()): try: - frappe.get_doc(address).insert(ignore_mandatory=True) + address_doc = frappe.get_doc(address) + address_doc.insert(ignore_mandatory=True) except: - try: - gstin = address.pop("gstin", None) - frappe.get_doc(address).insert(ignore_mandatory=True) - self.log({"address": address, "message": "Invalid GSTIN: {}. Address was created without GSTIN".format(gstin)}) - except: - self.log(address) + self.log(address_doc) def create_items_uoms(items_file_url, uoms_file_url): uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url}) for uom in json.loads(uoms_file.get_content()): if not frappe.db.exists(uom): try: - frappe.get_doc(uom).insert() + uom_doc = frappe.get_doc(uom) + uom_doc.insert() except: - self.log(uom) + self.log(uom_doc) items_file = frappe.get_doc("File", {"file_url": items_file_url}) for item in json.loads(items_file.get_content()): try: - frappe.get_doc(item).insert() + item_doc = frappe.get_doc(item) + item_doc.insert() except: - self.log(item) + self.log(item_doc) try: self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4) @@ -299,6 +318,7 @@ class TallyMigration(Document): self.publish("Import Master Data", _("Done"), 4, 4) + self.set_account_defaults() self.is_master_data_imported = 1 except: @@ -468,13 +488,13 @@ class TallyMigration(Document): oldest_year = new_year def create_custom_fields(doctypes): + df = { + "fieldtype": "Data", + "fieldname": "tally_guid", + "read_only": 1, + "label": "Tally GUID" + } for doctype in doctypes: - df = { - "fieldtype": "Data", - "fieldname": "tally_guid", - "read_only": 1, - "label": "Tally GUID" - } create_custom_field(doctype, df) def create_price_list(): @@ -521,11 +541,12 @@ class TallyMigration(Document): for index, voucher in enumerate(chunk, start=start): try: - doc = frappe.get_doc(voucher).insert() - doc.submit() + voucher_doc = frappe.get_doc(voucher) + voucher_doc.insert() + voucher_doc.submit() self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total) except: - self.log(voucher) + self.log(voucher_doc) if is_last: self.status = "" @@ -551,9 +572,19 @@ class TallyMigration(Document): frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600) def log(self, data=None): - data = data or self.status - message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) - return frappe.log_error(title="Tally Migration Error", message=message) + if isinstance(data, frappe.model.document.Document): + if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: + failed_import_log = json.loads(self.failed_import_log) + failed_import_log.append({ + "doc": data.as_dict(), + "exc": traceback.format_exc() + }) + self.failed_import_log = json.dumps(failed_import_log) + self.save() + else: + data = data or self.status + message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) + return frappe.log_error(title="Tally Migration Error", message=message) def set_status(self, status=""): self.status = status From 2ab6c8fa60937dbc940b612a6d8cab5e3b0dfcd9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 23 Apr 2020 14:07:14 +0530 Subject: [PATCH 591/929] fix: set default round off acount (cherry picked from commit 38c677689dd54d91e0255700d94a68b0e92b3c1e) --- .../doctype/tally_migration/tally_migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 32d11681fb0..5a5759160b5 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -510,7 +510,7 @@ class TallyMigration(Document): try: frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable") frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable") - frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account) + frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.default_round_off_account) vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers}) vouchers = json.loads(vouchers_file.get_content()) From 0a2bbfaf243b992701efc5f149829c504b672f7e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 27 Apr 2020 13:12:38 +0530 Subject: [PATCH 592/929] feat: error log handling fix: toggle required accounts (cherry picked from commit 82e7a9de2856a27ed497d7559fe775524a1c54e0) --- .../tally_migration/tally_migration.js | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d84c8234efa..d06fe537ad1 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -1,7 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Tally Migration', { +frappe.ui.form.on("Tally Migration", { onload: function (frm) { let reload_status = true; frappe.realtime.on("tally_migration_progress_update", function (data) { @@ -36,6 +36,10 @@ frappe.ui.form.on('Tally Migration', { }); }, refresh: function (frm) { + frm.trigger("show_import_log"); + ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { + frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) + }) if (frm.doc.master_data && !frm.doc.is_master_data_imported) { if (frm.doc.is_master_data_processed) { if (frm.doc.status != "Importing Master Data") { @@ -71,5 +75,101 @@ frappe.ui.form.on('Tally Migration', { frm.reload_doc(); } ); + }, + show_import_log(frm) { + let index = 0; + let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); + let logs = import_log.slice(0, 20); + let hidden_logs = import_log.slice(20); + + frm.toggle_display("import_log_section", logs.length > 0); + + + const getError = (traceback) => { + let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 + let error_line = traceback.substr(exc_error_idx) + let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; + + return error_line.slice(split_str_idx).trim(); + } + + const cleanDoc = (obj) => { + let temp = obj; + $.each(temp, function(key, value){ + if (value === "" || value === null){ + delete obj[key]; + } else if (Object.prototype.toString.call(value) === '[object Object]') { + cleanDoc(value); + } else if ($.isArray(value)) { + $.each(value, function (k,v) { cleanDoc(v); }); + } + }); + return temp; + }; + + let rows = logs + .map(({ doc, exc }) => { + let id = frappe.dom.get_unique_id(); + let traceback = exc; + + let error_message = getError(traceback); + index++; + + let html = ` + +
    +
    +
    ${traceback}
    +
    +
    `; + + let show_doc = ` + +
    +
    +
    ${JSON.stringify(cleanDoc(doc), null, 1)}
    +
    +
    `; + + let create_button = ` + ` + + return `
    + + + + + `; + }) + .join(""); + + frm.get_field("failed_import_preview").$wrapper.html(` +
    {{ __("Leave Type") }}{{ __("Total Allocated Leaves") }}{{ __("Used Leaves") }}{{ __("Pending Leaves") }}{{ __("Available Leaves") }}{{ __("Leave Type") }}{{ __("Total Allocated Leaves") }}{{ __("Expired Leaves") }}{{ __("Used Leaves") }}{{ __("Pending Leaves") }}{{ __("Available Leaves") }}
    {%= key %} {%= value["total_leaves"] %} {%= value["expired_leaves"] %} {%= value["leaves_taken"] %} {%= value["pending_leaves"] %} {%= value["remaining_leaves"] %}
    ${index} +
    ${doc.doctype}
    +
    +
    ${error_message}
    +
    ${html}
    +
    ${show_doc}
    +
    +
    ${create_button}
    +
    + + + + + + + ${rows} + + + +
    ${__("#")}${__("DocType")}${__("Error Message")}${__("Create")}
    And ${hidden_logs.length} more others
    + `); } }); From 7ee6e8360583e3f1130f58565bd1a491c7bffea0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 27 Apr 2020 13:42:00 +0530 Subject: [PATCH 593/929] perf: reduce failed_error_log size (cherry picked from commit 0805307514a7c9cafa6af9d6b5e0a490d42c5800) --- .../doctype/tally_migration/tally_migration.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 5a5759160b5..46382c1e8f5 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -575,11 +575,14 @@ class TallyMigration(Document): if isinstance(data, frappe.model.document.Document): if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: failed_import_log = json.loads(self.failed_import_log) + doc = data.as_dict() + doc_fields = { x.fieldname for x in frappe.get_doc("DocType", doc.doctype).fields } + stripped_doc = { k: v for k, v in doc.items() if k in doc_fields } failed_import_log.append({ - "doc": data.as_dict(), + "doc": stripped_doc, "exc": traceback.format_exc() }) - self.failed_import_log = json.dumps(failed_import_log) + self.failed_import_log = json.dumps(failed_import_log, separators=(',', ':')) self.save() else: data = data or self.status From 2f5208b08d84567e23fab112258158ed4891f02d Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 15:51:40 +0530 Subject: [PATCH 594/929] feat: order failed import log by creation, handle rollbacks and commits (cherry picked from commit 1b5eea691cf166562efb1de019599da1d365aede) --- .../doctype/tally_migration/tally_migration.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 46382c1e8f5..cbc6019c6b4 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -29,6 +29,11 @@ VOUCHER_CHUNK_SIZE = 500 class TallyMigration(Document): + def validate(self): + failed_import_log = json.loads(self.failed_import_log) + sorted_failed_import_log = sorted(failed_import_log, key=lambda row: row["doc"]["creation"]) + self.failed_import_log = json.dumps(sorted_failed_import_log) + def autoname(self): if not self.name: self.name = "Tally Migration on " + format_datetime(self.creation) @@ -320,9 +325,11 @@ class TallyMigration(Document): self.set_account_defaults() self.is_master_data_imported = 1 + frappe.db.commit() except: self.publish("Import Master Data", _("Process Failed"), -1, 5) + frappe.db.rollback() self.log() finally: @@ -343,7 +350,9 @@ class TallyMigration(Document): processed_voucher = function(voucher) if processed_voucher: vouchers.append(processed_voucher) + frappe.db.commit() except: + frappe.db.rollback() self.log(voucher) return vouchers @@ -545,7 +554,9 @@ class TallyMigration(Document): voucher_doc.insert() voucher_doc.submit() self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total) + frappe.db.commit() except: + frappe.db.rollback() self.log(voucher_doc) if is_last: @@ -576,14 +587,14 @@ class TallyMigration(Document): if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: failed_import_log = json.loads(self.failed_import_log) doc = data.as_dict() - doc_fields = { x.fieldname for x in frappe.get_doc("DocType", doc.doctype).fields } - stripped_doc = { k: v for k, v in doc.items() if k in doc_fields } failed_import_log.append({ - "doc": stripped_doc, + "doc": doc, "exc": traceback.format_exc() }) self.failed_import_log = json.dumps(failed_import_log, separators=(',', ':')) self.save() + frappe.db.commit() + else: data = data or self.status message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) From 28a6e1deeca70c78aa4b087cd3575e1c8edfcb38 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 16:01:59 +0530 Subject: [PATCH 595/929] feat: maintain logs of errors successfully fixed (cherry picked from commit 640636bbcd6f84c12c5ed0e74a3e257acb731f52) --- .../tally_migration/tally_migration.json | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json index 005c8a98c6b..417d9437926 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json @@ -38,7 +38,9 @@ "is_day_book_data_imported", "import_log_section", "failed_import_log", - "failed_import_preview" + "fixed_errors_log", + "failed_import_preview", + "fixed_error_log_preview" ], "fields": [ { @@ -220,7 +222,6 @@ "fieldname": "failed_import_log", "fieldtype": "Code", "hidden": 1, - "label": "Failed Import Log", "options": "JSON" }, { @@ -238,10 +239,22 @@ "fieldtype": "Link", "label": "Default Round Off Account", "options": "Account" + }, + { + "default": "[]", + "fieldname": "fixed_errors_log", + "fieldtype": "Code", + "hidden": 1, + "options": "JSON" + }, + { + "fieldname": "fixed_error_log_preview", + "fieldtype": "HTML", + "label": "Fixed Error Log" } ], "links": [], - "modified": "2020-04-22 16:01:13.718842", + "modified": "2020-04-28 00:29:18.039826", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Tally Migration", From 6950c10c6fc394f66d49a68eb4fc919fb18ea5ca Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 16:02:50 +0530 Subject: [PATCH 596/929] feat: handle resolve and unresolved errors restructured and reused components (cherry picked from commit 2e1b531f4fce10bdaf74218fa7a0d260d67c1d7a) --- .../tally_migration/tally_migration.js | 299 +++++++++++++----- 1 file changed, 217 insertions(+), 82 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d06fe537ad1..9235bab0683 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -35,8 +35,10 @@ frappe.ui.form.on("Tally Migration", { } }); }, + refresh: function (frm) { - frm.trigger("show_import_log"); + frm.trigger("show_logs_preview"); + ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) }) @@ -63,6 +65,7 @@ frappe.ui.form.on("Tally Migration", { } } }, + add_button: function (frm, label, method) { frm.add_custom_button( label, @@ -76,94 +79,30 @@ frappe.ui.form.on("Tally Migration", { } ); }, - show_import_log(frm) { - let index = 0; - let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); - let logs = import_log.slice(0, 20); - let hidden_logs = import_log.slice(20); - frm.toggle_display("import_log_section", logs.length > 0); - - - const getError = (traceback) => { - let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 - let error_line = traceback.substr(exc_error_idx) - let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; - - return error_line.slice(split_str_idx).trim(); + render_html_table(frm, shown_logs, hidden_logs, field) { + if (shown_logs && shown_logs.length > 0) { + frm.toggle_display(field, true); + } else { + frm.toggle_display(field, false); + return } + let rows = get_html_rows(shown_logs, field); + let rows_head; - const cleanDoc = (obj) => { - let temp = obj; - $.each(temp, function(key, value){ - if (value === "" || value === null){ - delete obj[key]; - } else if (Object.prototype.toString.call(value) === '[object Object]') { - cleanDoc(value); - } else if ($.isArray(value)) { - $.each(value, function (k,v) { cleanDoc(v); }); - } - }); - return temp; - }; - - let rows = logs - .map(({ doc, exc }) => { - let id = frappe.dom.get_unique_id(); - let traceback = exc; - - let error_message = getError(traceback); - index++; - - let html = ` - -
    -
    -
    ${traceback}
    -
    -
    `; - - let show_doc = ` - -
    -
    -
    ${JSON.stringify(cleanDoc(doc), null, 1)}
    -
    -
    `; - - let create_button = ` - ` - - return ` - ${index} - -
    ${doc.doctype}
    - - -
    ${error_message}
    -
    ${html}
    -
    ${show_doc}
    - - -
    ${create_button}
    - - `; - }) - .join(""); - - frm.get_field("failed_import_preview").$wrapper.html(` + if (field === "fixed_error_log_preview") { + rows_head = `${__("Meta Data")} + ${__("Unresolve")}` + } else { + rows_head = `${__("Error Message")} + ${__("Create")}` + } + frm.get_field(field).$wrapper.html(` - - + ${rows_head} ${rows} @@ -171,5 +110,201 @@ frappe.ui.form.on("Tally Migration", {
    ${__("#")} ${__("DocType")}${__("Error Message")}${__("Create")}
    `); + }, + + show_error_summary() { + let summary = import_log.reduce((summary, row) => { + if (row.doc) { + if (summary[row.doc.doctype]) { + summary[row.doc.doctype] += 1; + } else { + summary[row.doc.doctype] = 1; + } + } + return summary + }, {}); + console.table(summary); + }, + + show_logs_preview(frm) { + let empty = "[]"; + let import_log = frm.doc.failed_import_log || empty; + let completed_log = frm.doc.fixed_errors_log || empty; + let render_section = !(import_log === completed_log && import_log === empty); + + frm.toggle_display("import_log_section", render_section); + + if (render_section) { + frm.trigger("show_errored_import_log"); + frm.trigger("show_fixed_errors_log"); + } + }, + + show_errored_import_log(frm) { + let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); + + let logs = import_log.slice(0, 20); + let hidden_logs = import_log.slice(20); + + frm.events.render_html_table(frm, logs, hidden_logs, "failed_import_preview"); + }, + + show_fixed_errors_log(frm) { + let completed_log = JSON.parse(frm.doc.fixed_errors_log || "[]"); + let logs = completed_log.slice(0, 20); + let hidden_logs = completed_log.slice(20); + + frm.events.render_html_table(frm, logs, hidden_logs, "fixed_error_log_preview"); } }); + +const getError = (traceback) => { + /* Extracts the Error Message from the Python Traceback or Solved error */ + let is_multiline = traceback.trim().indexOf("\n") != -1; + let message; + + if (is_multiline) { + let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 + let error_line = traceback.substr(exc_error_idx) + let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; + message = error_line.slice(split_str_idx).trim(); + } else { + message = traceback; + } + + return message +} + +const cleanDoc = (obj) => { + /* Strips all null and empty values of your JSON object */ + let temp = obj; + $.each(temp, function(key, value){ + if (value === "" || value === null){ + delete obj[key]; + } else if (Object.prototype.toString.call(value) === '[object Object]') { + cleanDoc(value); + } else if ($.isArray(value)) { + $.each(value, function (k,v) { cleanDoc(v); }); + } + }); + return temp; +} + +window.unresolve = (document) => { + let frm = cur_frm; + let failed_log = JSON.parse(frm.doc.failed_import_log); + let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + + let modified_fixed_log = fixed_log.filter(row => { + if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + return row + } + }); + + failed_log.push({ doc: document, exc: `Marked unresolved on ${Date()}` }); + + frm.doc.failed_import_log = JSON.stringify(failed_log); + frm.doc.fixed_errors_log = JSON.stringify(modified_fixed_log); + + // frm.trigger('show_logs_preview') + frm.dirty(); + frm.save(); +} + +window.create_new_doc = (doctype, document) => { + let frm = cur_frm; + let failed_log = JSON.parse(frm.doc.failed_import_log); + let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + + let modified_failed_log = failed_log.filter(row => { + if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + return row + } + }); + fixed_log.push({ doc: document, exc: `Solved on ${Date()}` }); + + frm.doc.failed_import_log = JSON.stringify(modified_failed_log); + frm.doc.fixed_errors_log = JSON.stringify(fixed_log); + + // frm.trigger('show_logs_preview') + frm.dirty(); + frm.save(); + frappe.new_doc(doctype, document); +} + +const get_html_rows = (logs, field) => { + let index = 0; + let rows = logs + .map(({ doc, exc }) => { + let id = frappe.dom.get_unique_id(); + let traceback = exc; + + let error_message = getError(traceback); + index++; + + let show_traceback = ` + +
    +
    +
    ${traceback}
    +
    +
    `; + + let show_doc = ` + +
    +
    +
    ${JSON.stringify(cleanDoc(doc), null, 1)}
    +
    +
    `; + + let create_button = ` + ` + + let mark_as_unresolved = ` + ` + + if (field === "fixed_error_log_preview") { + return ` + ${index} + +
    ${doc.doctype}
    + + +
    ${error_message}
    +
    ${show_traceback}
    +
    ${show_doc}
    + + +
    ${mark_as_unresolved}
    + + `; + } else { + return ` + ${index} + +
    ${doc.doctype}
    + + +
    ${error_message}
    +
    ${show_traceback}
    +
    ${show_doc}
    + + +
    ${create_button}
    + + `; + } + }) + .join(""); + + return rows +} \ No newline at end of file From d3d511d31a4b5b5e57aa0de7c59a32bd28a41675 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 16:32:15 +0530 Subject: [PATCH 597/929] fix: run summary logger style: indent fixes (cherry picked from commit 27b7172fa4b888feaf47cf2875405079e83196ef) --- .../doctype/tally_migration/tally_migration.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 9235bab0683..bf8ae8c1c6c 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -133,7 +133,7 @@ frappe.ui.form.on("Tally Migration", { let render_section = !(import_log === completed_log && import_log === empty); frm.toggle_display("import_log_section", render_section); - + frm.trigger("show_error_summary"); if (render_section) { frm.trigger("show_errored_import_log"); frm.trigger("show_fixed_errors_log"); @@ -302,9 +302,8 @@ const get_html_rows = (logs, field) => {
    ${create_button}
    `; - } - }) - .join(""); + } + }).join(""); return rows } \ No newline at end of file From 18d432a843a4a1974737cb2bb1040f7ab9a85a0b Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 29 Apr 2020 04:01:24 +0530 Subject: [PATCH 598/929] fix: table cleanups, updated summary block (cherry picked from commit 0d61b35b0bc26ce509801b747bdc2d1d349b7d02) --- .../tally_migration/tally_migration.js | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index bf8ae8c1c6c..9cc3c98a569 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -88,32 +88,38 @@ frappe.ui.form.on("Tally Migration", { return } let rows = get_html_rows(shown_logs, field); - let rows_head; + let rows_head, table_caption; + + let table_footer = (hidden_logs && (hidden_logs.length > 0)) ? ` + And ${hidden_logs.length} more others + `: ""; if (field === "fixed_error_log_preview") { rows_head = `${__("Meta Data")} ${__("Unresolve")}` + table_caption = "Resolved Issues" } else { rows_head = `${__("Error Message")} ${__("Create")}` + table_caption = "Error Log" } + frm.get_field(field).$wrapper.html(` + ${rows_head} ${rows} - - - + ${table_footer}
    ${table_caption}
    ${__("#")} ${__("DocType")}
    And ${hidden_logs.length} more others
    `); }, - show_error_summary() { - let summary = import_log.reduce((summary, row) => { + show_error_summary(frm) { + let summary = JSON.parse(frm.doc.failed_import_log).reduce((summary, row) => { if (row.doc) { if (summary[row.doc.doctype]) { summary[row.doc.doctype] += 1; @@ -280,7 +286,6 @@ const get_html_rows = (logs, field) => {
    ${error_message}
    -
    ${show_traceback}
    ${show_doc}
    @@ -302,8 +307,8 @@ const get_html_rows = (logs, field) => {
    ${create_button}
    `; - } - }).join(""); + } + }).join(""); return rows } \ No newline at end of file From adb864828aed8c6f49a0a16f406ea07cf643f8a4 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 29 Apr 2020 10:25:18 +0530 Subject: [PATCH 599/929] perf: reduce JS memory heap and maintain namespaces (cherry picked from commit 6b850b7e71e30c2513ae8f50152eb67788604eb0) --- .../tally_migration/tally_migration.js | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 9cc3c98a569..0cb4995ac95 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -1,6 +1,8 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +frappe.provide("erpnext.tally_migration"); + frappe.ui.form.on("Tally Migration", { onload: function (frm) { let reload_status = true; @@ -38,10 +40,13 @@ frappe.ui.form.on("Tally Migration", { refresh: function (frm) { frm.trigger("show_logs_preview"); + erpnext.tally_migration.failed_import_log = JSON.parse(frm.doc.failed_import_log); + erpnext.tally_migration.fixed_errors_log = JSON.parse(frm.doc.fixed_errors_log); ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) }) + if (frm.doc.master_data && !frm.doc.is_master_data_imported) { if (frm.doc.is_master_data_processed) { if (frm.doc.status != "Importing Master Data") { @@ -53,6 +58,7 @@ frappe.ui.form.on("Tally Migration", { } } } + if (frm.doc.day_book_data && !frm.doc.is_day_book_data_imported) { if (frm.doc.is_day_book_data_processed) { if (frm.doc.status != "Importing Day Book Data") { @@ -87,7 +93,7 @@ frappe.ui.form.on("Tally Migration", { frm.toggle_display(field, false); return } - let rows = get_html_rows(shown_logs, field); + let rows = erpnext.tally_migration.get_html_rows(shown_logs, field); let rows_head, table_caption; let table_footer = (hidden_logs && (hidden_logs.length > 0)) ? ` @@ -119,7 +125,7 @@ frappe.ui.form.on("Tally Migration", { }, show_error_summary(frm) { - let summary = JSON.parse(frm.doc.failed_import_log).reduce((summary, row) => { + let summary = erpnext.tally_migration.failed_import_log.reduce((summary, row) => { if (row.doc) { if (summary[row.doc.doctype]) { summary[row.doc.doctype] += 1; @@ -147,8 +153,7 @@ frappe.ui.form.on("Tally Migration", { }, show_errored_import_log(frm) { - let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); - + let import_log = erpnext.tally_migration.failed_import_log; let logs = import_log.slice(0, 20); let hidden_logs = import_log.slice(20); @@ -156,7 +161,7 @@ frappe.ui.form.on("Tally Migration", { }, show_fixed_errors_log(frm) { - let completed_log = JSON.parse(frm.doc.fixed_errors_log || "[]"); + let completed_log = erpnext.tally_migration.fixed_errors_log; let logs = completed_log.slice(0, 20); let hidden_logs = completed_log.slice(20); @@ -164,7 +169,7 @@ frappe.ui.form.on("Tally Migration", { } }); -const getError = (traceback) => { +erpnext.tally_migration.getError = (traceback) => { /* Extracts the Error Message from the Python Traceback or Solved error */ let is_multiline = traceback.trim().indexOf("\n") != -1; let message; @@ -181,28 +186,28 @@ const getError = (traceback) => { return message } -const cleanDoc = (obj) => { +erpnext.tally_migration.cleanDoc = (obj) => { /* Strips all null and empty values of your JSON object */ let temp = obj; $.each(temp, function(key, value){ if (value === "" || value === null){ delete obj[key]; } else if (Object.prototype.toString.call(value) === '[object Object]') { - cleanDoc(value); + erpnext.tally_migration.cleanDoc(value); } else if ($.isArray(value)) { - $.each(value, function (k,v) { cleanDoc(v); }); + $.each(value, function (k,v) { erpnext.tally_migration.cleanDoc(v); }); } }); return temp; } -window.unresolve = (document) => { +erpnext.tally_migration.unresolve = (document) => { let frm = cur_frm; - let failed_log = JSON.parse(frm.doc.failed_import_log); - let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + let failed_log = erpnext.tally_migration.failed_import_log; + let fixed_log = erpnext.tally_migration.fixed_errors_log; let modified_fixed_log = fixed_log.filter(row => { - if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) { return row } }); @@ -217,13 +222,13 @@ window.unresolve = (document) => { frm.save(); } -window.create_new_doc = (doctype, document) => { +erpnext.tally_migration.resolve = (document) => { let frm = cur_frm; - let failed_log = JSON.parse(frm.doc.failed_import_log); - let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + let failed_log = erpnext.tally_migration.failed_import_log; + let fixed_log = erpnext.tally_migration.fixed_errors_log; let modified_failed_log = failed_log.filter(row => { - if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) { return row } }); @@ -235,17 +240,21 @@ window.create_new_doc = (doctype, document) => { // frm.trigger('show_logs_preview') frm.dirty(); frm.save(); +} + +erpnext.tally_migration.create_new_doc = (doctype, document) => { + erpnext.tally_migration.resolve(document); frappe.new_doc(doctype, document); } -const get_html_rows = (logs, field) => { +erpnext.tally_migration.get_html_rows = (logs, field) => { let index = 0; let rows = logs .map(({ doc, exc }) => { let id = frappe.dom.get_unique_id(); let traceback = exc; - let error_message = getError(traceback); + let error_message = erpnext.tally_migration.getError(traceback); index++; let show_traceback = ` @@ -264,17 +273,17 @@ const get_html_rows = (logs, field) => {
    -
    ${JSON.stringify(cleanDoc(doc), null, 1)}
    +
    ${JSON.stringify(erpnext.tally_migration.cleanDoc(doc), null, 1)}
    `; let create_button = ` - ` let mark_as_unresolved = ` - ` From 0a7afd77e26d6b79bf284e7acf12b2581d949de2 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 29 Apr 2020 11:21:16 +0530 Subject: [PATCH 600/929] fix: toggle read only for accounts post daybook data processing style: code comments and updates (cherry picked from commit f4926a8205184e21641a1770b9096a8804f2cd64) --- .../doctype/tally_migration/tally_migration.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 0cb4995ac95..e94cca54c52 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -45,6 +45,7 @@ frappe.ui.form.on("Tally Migration", { ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) + frm.toggle_enable(account, frm.doc.is_day_book_data_processed != 1) }) if (frm.doc.master_data && !frm.doc.is_master_data_imported) { @@ -145,8 +146,8 @@ frappe.ui.form.on("Tally Migration", { let render_section = !(import_log === completed_log && import_log === empty); frm.toggle_display("import_log_section", render_section); - frm.trigger("show_error_summary"); if (render_section) { + frm.trigger("show_error_summary"); frm.trigger("show_errored_import_log"); frm.trigger("show_fixed_errors_log"); } @@ -202,6 +203,7 @@ erpnext.tally_migration.cleanDoc = (obj) => { } erpnext.tally_migration.unresolve = (document) => { + /* Mark document migration as unresolved ie. move to failed error log */ let frm = cur_frm; let failed_log = erpnext.tally_migration.failed_import_log; let fixed_log = erpnext.tally_migration.fixed_errors_log; @@ -217,12 +219,12 @@ erpnext.tally_migration.unresolve = (document) => { frm.doc.failed_import_log = JSON.stringify(failed_log); frm.doc.fixed_errors_log = JSON.stringify(modified_fixed_log); - // frm.trigger('show_logs_preview') frm.dirty(); frm.save(); } erpnext.tally_migration.resolve = (document) => { + /* Mark document migration as resolved ie. move to fixed error log */ let frm = cur_frm; let failed_log = erpnext.tally_migration.failed_import_log; let fixed_log = erpnext.tally_migration.fixed_errors_log; @@ -237,12 +239,12 @@ erpnext.tally_migration.resolve = (document) => { frm.doc.failed_import_log = JSON.stringify(modified_failed_log); frm.doc.fixed_errors_log = JSON.stringify(fixed_log); - // frm.trigger('show_logs_preview') frm.dirty(); frm.save(); } erpnext.tally_migration.create_new_doc = (doctype, document) => { + /* Mark as resolved and create new document */ erpnext.tally_migration.resolve(document); frappe.new_doc(doctype, document); } From d50cceaafa6942df70134a4535dc0353c1a1065a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 30 Apr 2020 04:07:14 +0530 Subject: [PATCH 601/929] feat: prompt that company and COA will be overwritten (cherry picked from commit 51305a028be5f7a766dd099f3d93fbea0c28d93d) --- .../doctype/tally_migration/tally_migration.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index e94cca54c52..d9811b58dde 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -73,6 +73,16 @@ frappe.ui.form.on("Tally Migration", { } }, + erpnext_company: function (frm) { + frappe.db.exists("Company", frm.doc.erpnext_company).then(exists => { + if (exists) { + frappe.msgprint( + __("Company {0} already exists. Continuing will overwrite the Company and Chart of Accounts", [frm.doc.erpnext_company]), + ); + } + }); + }, + add_button: function (frm, label, method) { frm.add_custom_button( label, From 8ea9b0a4158b5bb4fc8ab200418b8716f6e58a60 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 1 May 2020 14:39:11 +0530 Subject: [PATCH 602/929] feat: maintain tally voucher numbers feat: create seperate customer and supplier entries for same party if case exists (cherry picked from commit af612ddb6de0c59381d36f1a1212188401990f05) --- .../tally_migration/tally_migration.py | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index cbc6019c6b4..393c5d4b02b 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -145,14 +145,18 @@ class TallyMigration(Document): def remove_parties(parents, children, group_set): customers, suppliers = set(), set() for account in parents: + found = False if self.tally_creditors_account in parents[account]: - children.pop(account, None) + found = True if account not in group_set: suppliers.add(account) - elif self.tally_debtors_account in parents[account]: - children.pop(account, None) + if self.tally_debtors_account in parents[account]: + found = True if account not in group_set: customers.add(account) + if found: + children.pop(account, None) + return children, customers, suppliers def traverse(tree, children, accounts, roots, group_set): @@ -170,6 +174,7 @@ class TallyMigration(Document): parties, addresses = [], [] for account in collection.find_all("LEDGER"): party_type = None + links = [] if account.NAME.string.strip() in customers: party_type = "Customer" parties.append({ @@ -180,7 +185,9 @@ class TallyMigration(Document): "territory": "All Territories", "customer_type": "Individual", }) - elif account.NAME.string.strip() in suppliers: + links.append({"link_doctype": party_type, "link_name": account["NAME"]}) + + if account.NAME.string.strip() in suppliers: party_type = "Supplier" parties.append({ "doctype": party_type, @@ -189,6 +196,8 @@ class TallyMigration(Document): "supplier_group": "All Supplier Groups", "supplier_type": "Individual", }) + links.append({"link_doctype": party_type, "link_name": account["NAME"]}) + if party_type: address = "\n".join([a.string.strip() for a in account.find_all("ADDRESS")]) addresses.append({ @@ -202,7 +211,7 @@ class TallyMigration(Document): "mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, "phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, "gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None, - "links": [{"link_doctype": party_type, "link_name": account["NAME"]}], + "links": links }) return parties, addresses @@ -378,6 +387,7 @@ class TallyMigration(Document): journal_entry = { "doctype": "Journal Entry", "tally_guid": voucher.GUID.string.strip(), + "tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "", "posting_date": voucher.DATE.string.strip(), "company": self.erpnext_company, "accounts": accounts, @@ -406,6 +416,7 @@ class TallyMigration(Document): "doctype": doctype, party_field: voucher.PARTYNAME.string.strip(), "tally_guid": voucher.GUID.string.strip(), + "tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "", "posting_date": voucher.DATE.string.strip(), "due_date": voucher.DATE.string.strip(), "items": get_voucher_items(voucher, doctype), @@ -497,14 +508,21 @@ class TallyMigration(Document): oldest_year = new_year def create_custom_fields(doctypes): - df = { + tally_guid_df = { "fieldtype": "Data", "fieldname": "tally_guid", "read_only": 1, "label": "Tally GUID" } - for doctype in doctypes: - create_custom_field(doctype, df) + tally_voucher_no_df = { + "fieldtype": "Data", + "fieldname": "tally_voucher_no", + "read_only": 1, + "label": "Tally Voucher Number" + } + for df in [tally_guid_df, tally_voucher_no_df]: + for doctype in doctypes: + create_custom_field(doctype, df) def create_price_list(): frappe.get_doc({ From 43a19da27aed60abc2844823ab41e274702ae6c2 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Sat, 2 May 2020 18:58:33 +0530 Subject: [PATCH 603/929] fix: un-using buggy JS frappe.new_doc (cherry picked from commit 0c353a64a44fb5567e1de315ca0fef0092c0aa68) --- .../tally_migration/tally_migration.js | 20 ++++++++++++++++--- .../tally_migration/tally_migration.py | 10 ++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d9811b58dde..fd16d1e84aa 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -253,10 +253,24 @@ erpnext.tally_migration.resolve = (document) => { frm.save(); } -erpnext.tally_migration.create_new_doc = (doctype, document) => { +erpnext.tally_migration.create_new_doc = (document) => { /* Mark as resolved and create new document */ erpnext.tally_migration.resolve(document); - frappe.new_doc(doctype, document); + return frappe.call({ + type: "POST", + method: 'erpnext.erpnext_integrations.doctype.tally_migration.tally_migration.new_doc', + args: { + document + }, + freeze: true, + callback: function(r) { + if(!r.exc) { + frappe.model.sync(r.message); + frappe.get_doc(r.message.doctype, r.message.name).__run_link_triggers = true; + frappe.set_route("Form", r.message.doctype, r.message.name); + } + } + }); } erpnext.tally_migration.get_html_rows = (logs, field) => { @@ -290,7 +304,7 @@ erpnext.tally_migration.get_html_rows = (logs, field) => {
    `; let create_button = ` - ` diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 393c5d4b02b..d9c5852a6ed 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -28,6 +28,16 @@ PRIMARY_ACCOUNT = "Primary" VOUCHER_CHUNK_SIZE = 500 +@frappe.whitelist() +def new_doc(document): + document = json.loads(document) + doctype = document.pop("doctype") + document.pop("name", None) + doc = frappe.new_doc(doctype) + doc.update(document) + + return doc + class TallyMigration(Document): def validate(self): failed_import_log = json.loads(self.failed_import_log) From 2e825a108faaea413cb66525a342936fa715ec83 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Sat, 9 May 2020 12:05:33 +0530 Subject: [PATCH 604/929] chore: verbose error message for coa recursion (cherry picked from commit cb1376b036edb7d3161c331cdf4f7451a2d97a3e) --- .../doctype/tally_migration/tally_migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index d9c5852a6ed..462685f5e71 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -113,7 +113,7 @@ class TallyMigration(Document): try: coa = traverse({}, children, roots, roots, group_set) except RecursionError: - self.log() + self.log(_("Error occured while parsing Chart of Accounts: Please make sure that no two accounts have the same name")) for account in coa: coa[account]["root_type"] = root_type_map[account] From 99f5ece31cfeee9aa0fe867888327e7a250ba3af Mon Sep 17 00:00:00 2001 From: Myuddin khatri Date: Wed, 3 Jun 2020 11:14:49 +0530 Subject: [PATCH 605/929] fix(stock): able to create purchase invoice from purchase receipt dashboard able to create purchase invoice from purchase receipt dashboard --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index d5914f9b28d..89ca1bef856 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -25,7 +25,7 @@ frappe.ui.form.on("Purchase Receipt", { frm.custom_make_buttons = { 'Stock Entry': 'Return', - 'Purchase Invoice': 'Invoice' + 'Purchase Invoice': 'Purchase Invoice' }; frm.set_query("expense_account", "items", function() { From c417cf079c3edde6ab9e0ad7d99b883c27556236 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 3 Jun 2020 10:59:37 +0530 Subject: [PATCH 606/929] fix(India): Reverse charge mechanism for GST --- erpnext/hooks.py | 3 ++ .../gstr_3b_report/gstr_3b_report.html | 2 +- .../doctype/gstr_3b_report/gstr_3b_report.py | 12 ++--- erpnext/regional/india/utils.py | 52 ++++++++++++++++++- 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 6712842948e..5de2af51694 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -244,6 +244,9 @@ doc_events = { "on_cancel": "erpnext.regional.italy.utils.sales_invoice_on_cancel", "on_trash": "erpnext.regional.check_deletion_permission" }, + "Purchase Invoice": { + "on_submit": "erpnext.regional.india.utils.make_reverse_charge_entries" + }, "Payment Entry": { "on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status"], "on_trash": "erpnext.regional.check_deletion_permission" diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html index 35f9cf674ce..888b2da48eb 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html @@ -52,7 +52,7 @@ - (d) {{__("Inward Supplies(liable to reverse charge")}} + (d) {{__("Inward Supplies(liable to reverse charge)")}} {{ flt(data.sup_details.isup_rev.txval, 2) }} {{ flt(data.sup_details.isup_rev.iamt, 2) }} {{ flt(data.sup_details.isup_rev.camt, 2) }} diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 15dcbd5e154..2cdf29f4fee 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -158,7 +158,7 @@ class GSTR3BReport(Document): self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_det", ["Registered Regular"]) self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_zero", ["SEZ", "Deemed Export", "Overseas"]) - self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Registered Regular"], reverse_charge="Y") + self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Unregistered"], reverse_charge="Y") self.report_dict["sup_details"]["osup_nil_exmp"]["txval"] = flt(self.get_nil_rated_supply_value(), 2) self.set_itc_details(itc_details) @@ -196,11 +196,12 @@ class GSTR3BReport(Document): if d["ty"] == 'ISRC': reverse_charge = "Y" + itc_type = 'All Other ITC' + gst_category = 'Unregistered' else: reverse_charge = "N" for account_head in self.account_heads: - d["iamt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('igst_account')), {}).get("amount"), 2) d["camt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('cgst_account')), {}).get("amount"), 2) d["samt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('sgst_account')), {}).get("amount"), 2) @@ -274,17 +275,16 @@ class GSTR3BReport(Document): """ #nosec .format(doctype = doctype), (self.month_no, self.year, reverse_charge, self.company, self.gst_details.get("gstin")))) - def get_itc_details(self, reverse_charge='N'): - + def get_itc_details(self): itc_amount = frappe.db.sql(""" select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t - where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s + where s.docstatus = 1 and t.parent = s.name and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s and s.company_gstin = %s group by t.account_head, s.gst_category, s.eligibility_for_itc """, - (reverse_charge, self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) + (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) itc_details = {} diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index d22aa1eebb8..53200ba3a4b 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -9,6 +9,8 @@ from erpnext.hr.utils import get_salary_assignment from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.regional.india import number_state_mapping from six import string_types +from erpnext.accounts.general_ledger import make_gl_entries +from erpnext.accounts.utils import get_account_currency def validate_gstin_for_india(doc, method): if hasattr(doc, 'gst_state') and doc.gst_state: @@ -642,5 +644,53 @@ def get_gst_accounts(company, account_wise=False): elif val: gst_accounts[val] = acc - return gst_accounts + +def make_reverse_charge_entries(doc, method): + country = frappe.get_cached_value('Company', doc.company, 'country') + + if country != 'India': + return + + if doc.reverse_charge == 'Y': + gl_entries = [] + gst_accounts = get_gst_accounts(doc.company) + gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \ + + gst_accounts.get('igst_account') + + for tax in doc.get('taxes'): + if tax.category not in ("Total", "Valuation and Total"): + continue + + if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list: + account_currency = get_account_currency(tax.account_head) + + gl_entries.append(doc.get_gl_dict( + { + "account": tax.account_head, + "cost_center": tax.cost_center, + "posting_date": doc.posting_date, + "against": doc.supplier, + "credit": tax.base_tax_amount_after_discount_amount, + "credits_in_account_currency": tax.base_tax_amount_after_discount_amount \ + if account_currency==doc.company_currency \ + else tax.tax_amount_after_discount_amount + }, account_currency, item=tax) + ) + + gl_entries.append(doc.get_gl_dict( + { + "account": doc.credit_to if doc.doctype == 'Purchase Invoice' else doc.debit_to, + "cost_center": doc.cost_center, + "posting_date": doc.posting_date, + "party_type": 'Supplier', + "party": doc.supplier, + "against": tax.account_head, + "debit": tax.base_tax_amount_after_discount_amount, + "debit_in_account_currency": tax.base_tax_amount_after_discount_amount \ + if account_currency==doc.company_currency \ + else tax.tax_amount_after_discount_amount + }, account_currency, item=doc) + ) + + make_gl_entries(gl_entries) \ No newline at end of file From 4e3caa8ece68ab34cb0a2b1a0780a193a66fee17 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 3 Jun 2020 14:15:16 +0530 Subject: [PATCH 607/929] fix: cannot create existing assets --- erpnext/assets/doctype/asset/asset.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index d56549f7d90..ad7edd1ead2 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -126,6 +126,8 @@ class Asset(AccountsController): frappe.throw(_("Available-for-use Date should be after purchase date")) def validate_gross_and_purchase_amount(self): + if self.is_existing_asset: return + if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount: frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\ Please do not book expense of multiple assets against one single Asset.") From 1a1ce9a7438133d98b9569a58f3ad11a3fa4d19a Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 3 Jun 2020 14:32:07 +0530 Subject: [PATCH 608/929] fix: Apply shipping rule without address too --- erpnext/accounts/doctype/shipping_rule/shipping_rule.py | 4 +++- erpnext/public/js/controllers/transaction.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py index b2638c78279..d32a348741e 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py @@ -45,7 +45,9 @@ class ShippingRule(Document): shipping_amount = 0.0 by_value = False - self.validate_countries(doc) + if doc.get_shipping_address(): + # validate country only if there is address + self.validate_countries(doc) if self.calculate_based_on == 'Net Total': value = doc.base_net_total diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 801b6fce4a6..6e131272dcb 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -879,7 +879,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ shipping_rule: function() { var me = this; - if(this.frm.doc.shipping_rule && this.frm.doc.shipping_address) { + if(this.frm.doc.shipping_rule) { return this.frm.call({ doc: this.frm.doc, method: "apply_shipping_rule", From fc975cfc3a3369032f5568c5b48603dfeb22820f Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 3 Jun 2020 14:15:16 +0530 Subject: [PATCH 609/929] fix: cannot create existing assets --- erpnext/assets/doctype/asset/asset.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index d56549f7d90..ad7edd1ead2 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -126,6 +126,8 @@ class Asset(AccountsController): frappe.throw(_("Available-for-use Date should be after purchase date")) def validate_gross_and_purchase_amount(self): + if self.is_existing_asset: return + if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount: frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\ Please do not book expense of multiple assets against one single Asset.") From be104e5dcfe29ca5253bcb8539fa394e4a00d0ee Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Wed, 3 Jun 2020 15:30:41 +0550 Subject: [PATCH 610/929] bumped to version 12.9.4 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index f6fd5caca94..701217759c2 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.9.3' +__version__ = '12.9.4' def get_default_company(user=None): '''Get default company for user''' From 89dd0bad28d17c40a3d5a537fda812300cfb91ef Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 3 Jun 2020 17:13:58 +0530 Subject: [PATCH 611/929] fix: '>=' not supported between instances of 'str' and 'int' --- erpnext/stock/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 7f32b8d8bb8..95ecb7fecd7 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -230,12 +230,12 @@ def get_valuation_method(item_code): def get_fifo_rate(previous_stock_queue, qty): """get FIFO (average) Rate from Queue""" - if qty >= 0: + if flt(qty) >= 0: total = sum(f[0] for f in previous_stock_queue) return sum(flt(f[0]) * flt(f[1]) for f in previous_stock_queue) / flt(total) if total else 0.0 else: available_qty_for_outgoing, outgoing_cost = 0, 0 - qty_to_pop = abs(qty) + qty_to_pop = abs(flt(qty)) while qty_to_pop and previous_stock_queue: batch = previous_stock_queue[0] if 0 < batch[0] <= qty_to_pop: From c84961be85c68f368a272abf2b32fc5f38cadffb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 3 Jun 2020 21:51:21 +0530 Subject: [PATCH 612/929] fix: Error when no data is present in AR/AP reeport (cherry picked from commit 7135a75e5e28a5329b709174688761f78c307dff) --- .../report/accounts_receivable/accounts_receivable.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 4015b056c58..724e8b74437 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -169,9 +169,11 @@ class ReceivablePayableReport(object): def append_subtotal_row(self, party): sub_total_row = self.total_row_map.get(party) - self.data.append(sub_total_row) - self.data.append({}) - self.update_sub_total_row(sub_total_row, 'Total') + + if sub_total_row: + self.data.append(sub_total_row) + self.data.append({}) + self.update_sub_total_row(sub_total_row, 'Total') def get_voucher_balance(self, gle): if self.filters.get("sales_person"): @@ -232,7 +234,8 @@ class ReceivablePayableReport(object): if self.filters.get('group_by_party'): self.append_subtotal_row(self.previous_party) - self.data.append(self.total_row_map.get('Total')) + if self.data: + self.data.append(self.total_row_map.get('Total')) def append_row(self, row): self.allocate_future_payments(row) From 58a43be4f5acbf9e28f43d51391c242ccce8a67a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 4 Jun 2020 14:28:34 +0530 Subject: [PATCH 613/929] fix: import supplier invoice not working --- .../doctype/import_supplier_invoice/import_supplier_invoice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py index 203ea8c865a..e6b6f4d5da6 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py @@ -64,7 +64,8 @@ class ImportSupplierInvoice(Document): "buying_price_list": self.default_buying_price_list } - if not invoices_args.get("invoice_no", ''): return + if not invoices_args.get("bill_no", ''): + frappe.throw(_("Numero has not set in the XML file")) supp_dict = get_supplier_details(file_content) invoices_args["destination_code"] = get_destination_code_from_file(file_content) From 1ae2376a8337ada99c22d8b9ca517ffab54801cd Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 4 Jun 2020 14:46:50 +0530 Subject: [PATCH 614/929] fix: routing operations not added sequentially in the BOM --- erpnext/manufacturing/doctype/bom/bom.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 4f40fb71fc1..41881efec2d 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -101,13 +101,14 @@ class BOM(WebsiteGenerator): if self.routing: self.set("operations", []) for d in frappe.get_all("BOM Operation", fields = ["*"], - filters = {'parenttype': 'Routing', 'parent': self.routing}): + filters = {'parenttype': 'Routing', 'parent': self.routing}, order_by="idx"): child = self.append('operations', { "operation": d.operation, "workstation": d.workstation, "description": d.description, "time_in_mins": d.time_in_mins, - "batch_size": d.batch_size + "batch_size": d.batch_size, + "idx": d.idx }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) From efdb55a083d2cea29b423f1773e2fdede3b5caf6 Mon Sep 17 00:00:00 2001 From: P-Froggy <60393001+P-Froggy@users.noreply.github.com> Date: Fri, 5 Jun 2020 07:26:30 +0200 Subject: [PATCH 615/929] fix: Wrong Ordered-Status Indicator for Material Request Items (#22117) The indicator displaying if a material request item has been ordered or not (green/orange) used the wrong quantity field for determining the status. The qty field is not in stock uom while the ordered_qty field is. Now, the stock_qty field is used for correct comparison with ordered_qty. --- erpnext/stock/doctype/material_request/material_request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 6110ea822cf..b113781def3 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -17,7 +17,7 @@ frappe.ui.form.on('Material Request', { // formatter for material request item frm.set_indicator_formatter('item_code', - function(doc) { return (doc.qty<=doc.ordered_qty) ? "green" : "orange"; }); + function(doc) { return (doc.stock_qty<=doc.ordered_qty) ? "green" : "orange"; }); }, From 6732c9ab140dd43ccc5d0133e61974cccc34e9bc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 7 Jun 2020 18:59:06 +0530 Subject: [PATCH 616/929] fix: fetch tax lines within the shipping lines (#22138) (#22139) (cherry picked from commit 303112816771639a7808663170dac497b4f8e2d1) Co-authored-by: Mangesh-Khairnar --- .../connectors/shopify_connection.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index 3d92d55faaf..215f12c2c2f 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -241,14 +241,17 @@ def get_order_taxes(shopify_order, shopify_settings): return taxes def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings): + """Shipping lines represents the shipping details, + each such shipping detail consists of a list of tax_lines""" for shipping_charge in shipping_lines: - taxes.append({ - "charge_type": _("Actual"), - "account_head": get_tax_account_head(shipping_charge), - "description": shipping_charge["title"], - "tax_amount": shipping_charge["price"], - "cost_center": shopify_settings.cost_center - }) + for tax in shipping_charge.get("tax_lines"): + taxes.append({ + "charge_type": _("Actual"), + "account_head": get_tax_account_head(tax), + "description": tax["title"], + "tax_amount": tax["price"], + "cost_center": shopify_settings.cost_center + }) return taxes From 189663f576c764e421c5b9fa439e1105f2d2efdd Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 8 Jun 2020 13:02:00 +0530 Subject: [PATCH 617/929] fix: Finished Product Valuation at Repack --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- .../stock_entry_detail/stock_entry_detail.json | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index e48a2211e32..abc4fd0b377 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -497,7 +497,7 @@ class StockEntry(StockController): if raw_material_cost and self.purpose == "Manufacture": d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate")) d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount")) - elif self.purpose == "Repack" and total_fg_qty: + elif self.purpose == "Repack" and total_fg_qty and not d.set_basic_rate_manually: d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty) d.basic_amount = d.basic_rate * d.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 a848c80cf2c..9992d10febe 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -23,6 +23,7 @@ "image", "image_view", "quantity_and_rate", + "set_basic_rate_manually", "qty", "basic_rate", "basic_amount", @@ -490,12 +491,21 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:parent.purpose===\"Repack\" && doc.t_warehouse", + "fieldname": "set_basic_rate_manually", + "fieldtype": "Check", + "label": "Set Basic Rate Manually", + "show_days": 1, + "show_seconds": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-19 12:34:09.836295", + "modified": "2020-06-08 12:57:03.172887", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", From 5befddca39c643eb4db436cd8cd7b5fac4ec16a8 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Fri, 8 May 2020 18:01:18 +0800 Subject: [PATCH 618/929] fix: Tax Withholding Category Description default This commit fixes a bug that happens when a Purchase Invoice uses a Tax Withholding Category without a category_name. The category_name is used as the tax description which is a required field. The bug was fixed by using the Tax Withholding Category's name as the description if the category_name is empty. --- .../tax_withholding_category/tax_withholding_category.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 30bac5efa68..d4144c91044 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -58,7 +58,7 @@ def get_tax_withholding_details(tax_withholding_category, fiscal_year, company): "rate": tax_rate_detail.tax_withholding_rate, "threshold": tax_rate_detail.single_threshold, "cumulative_threshold": tax_rate_detail.cumulative_threshold, - "description": tax_withholding.category_name + "description": tax_withholding.category_name.strip() if tax_withholding.category_name.strip() else tax_withholding_category }) def get_tax_withholding_rates(tax_withholding, fiscal_year): @@ -225,4 +225,4 @@ def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount, certificate_limit > deducted_amount): valid = True - return valid \ No newline at end of file + return valid From 65fb3f2c48def516fdd33a26c747b59408ec344c Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sat, 16 May 2020 18:07:03 +0530 Subject: [PATCH 619/929] fix: Remove strip --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index d4144c91044..b539fff74e9 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -58,7 +58,7 @@ def get_tax_withholding_details(tax_withholding_category, fiscal_year, company): "rate": tax_rate_detail.tax_withholding_rate, "threshold": tax_rate_detail.single_threshold, "cumulative_threshold": tax_rate_detail.cumulative_threshold, - "description": tax_withholding.category_name.strip() if tax_withholding.category_name.strip() else tax_withholding_category + "description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category }) def get_tax_withholding_rates(tax_withholding, fiscal_year): From dfd410ded29751092c35ec0c0069fbe5b90802c6 Mon Sep 17 00:00:00 2001 From: Poranut Chollavorn Date: Tue, 9 Jun 2020 03:16:49 +0000 Subject: [PATCH 620/929] fix(pricing_rule): missing income account --- erpnext/accounts/doctype/pricing_rule/utils.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 53115f92d01..9876246c47b 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -13,7 +13,10 @@ from six import string_types import frappe from erpnext.setup.doctype.item_group.item_group import get_child_item_groups from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses -from erpnext.stock.get_item_details import get_conversion_factor +from erpnext.stock.get_item_details import get_conversion_factor, get_default_income_account +from erpnext.stock.doctype.item.item import get_item_defaults +from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults +from erpnext.setup.doctype.brand.brand import get_brand_defaults from frappe import _, throw from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today @@ -480,6 +483,14 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): if item_details.get("parenttype") == 'Sales Order': item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today() + company = args.get('company') or doc.company + item_details.free_item_data['income_account'] = get_default_income_account( + args=args, + item=get_item_defaults(free_item, company), + item_group=get_item_group_defaults(free_item, company), + brand=get_brand_defaults(free_item, company), + ) + def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False): if pricing_rule_args.get('item_code'): items = [d.item_code for d in doc.items From 9831c5beca7b0c2a4000210a582136157f1dbb41 Mon Sep 17 00:00:00 2001 From: Poranut Chollavorn Date: Tue, 9 Jun 2020 15:06:44 +0000 Subject: [PATCH 621/929] fix(loyalty): loyalty point entry use wrong tier --- .../doctype/loyalty_program/loyalty_program.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py index 563165b2cc8..6a82572a8d8 100644 --- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py @@ -41,10 +41,16 @@ def get_loyalty_program_details_with_points(customer, loyalty_program=None, expi loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program) lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry)) - tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules], - key=lambda rule:rule.min_spent, reverse=True) + # sort collection rule, first item on list will be lowest min_spent + tier_spent_level = sorted( + [d.as_dict() for d in loyalty_program.collection_rules], + key=lambda rule: rule.min_spent, reverse=False, + ) + + # looping and apply tier from highest min_spent for i, d in enumerate(tier_spent_level): - if i==0 or (lp_details.total_spent+current_transaction_amount) <= d.min_spent: + # if cumulative spend more than min_spent then continue to next tier + if (lp_details.total_spent + current_transaction_amount) >= d.min_spent: lp_details.tier_name = d.tier_name lp_details.collection_factor = d.collection_factor else: From 5a5361815fc7c7152da81f47a038fda9aa7334a6 Mon Sep 17 00:00:00 2001 From: Poranut Chollavorn Date: Tue, 9 Jun 2020 15:16:28 +0000 Subject: [PATCH 622/929] docs(loyalty): fix wrong comment --- erpnext/accounts/doctype/loyalty_program/loyalty_program.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py index 6a82572a8d8..d2d852229e2 100644 --- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py @@ -47,7 +47,7 @@ def get_loyalty_program_details_with_points(customer, loyalty_program=None, expi key=lambda rule: rule.min_spent, reverse=False, ) - # looping and apply tier from highest min_spent + # looping and apply tier from lowest min_spent for i, d in enumerate(tier_spent_level): # if cumulative spend more than min_spent then continue to next tier if (lp_details.total_spent + current_transaction_amount) >= d.min_spent: From 703cee6abfc37e1c15e5a743966d428a22e275de Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 10 Jun 2020 12:51:29 +0530 Subject: [PATCH 623/929] fix: update program course (#22166) (#22173) (cherry picked from commit 934e30ecac59dcd6aa5dcb06700c37a51209689c) Co-authored-by: Anupam Kumar --- .../program_course/program_course.json | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/erpnext/education/doctype/program_course/program_course.json b/erpnext/education/doctype/program_course/program_course.json index a24e88a8611..940358e4e9b 100644 --- a/erpnext/education/doctype/program_course/program_course.json +++ b/erpnext/education/doctype/program_course/program_course.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2015-09-07 14:37:01.886859", "doctype": "DocType", "editable_grid": 1, @@ -16,26 +17,33 @@ "in_list_view": 1, "label": "Course", "options": "Course", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, - { + { + "fetch_from": "course.course_name", "fieldname": "course_name", "fieldtype": "Data", "in_list_view": 1, "label": "Course Name", - "fetch_from": "course.course_name", - "read_only":1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "required", "fieldtype": "Check", "in_list_view": 1, - "label": "Mandatory" + "label": "Mandatory", + "show_days": 1, + "show_seconds": 1 } ], "istable": 1, - "modified": "2019-06-12 12:42:12.845972", + "links": [], + "modified": "2020-06-09 18:56:10.213241", "modified_by": "Administrator", "module": "Education", "name": "Program Course", @@ -45,4 +53,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file From c848b9775739b893e23d4367cdfaf63a9b2a60fb Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 10 Jun 2020 17:04:40 +0530 Subject: [PATCH 624/929] fix: Cannot read property 'has_batch_no' of undefined --- 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 6e131272dcb..0d3bbd658f7 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -539,7 +539,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if (show_batch_dialog) return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) .then((r) => { - if(r.message.has_batch_no || r.message.has_serial_no) { + if(r.message && + (r.message.has_batch_no || r.message.has_serial_no)) { frappe.flags.hide_serial_batch_dialog = false; } }); From 8710866886249eae43b2ac2c18fab2f4f8b36ca3 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 10 Jun 2020 18:22:59 +0530 Subject: [PATCH 625/929] fix: Prioritize Default Customer Price List in Portal --- erpnext/shopping_cart/cart.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index a33f4a12242..e5b0abb1ed8 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -337,21 +337,17 @@ def set_price_list_and_rate(quotation, cart_settings): def _set_price_list(cart_settings, quotation=None): """Set price list based on customer or shopping cart default""" from erpnext.accounts.party import get_default_price_list - - # check if customer price list exists + party_name = quotation.get("party_name") if quotation else get_party().get("name") selling_price_list = None - if quotation and quotation.get("party_name"): - selling_price_list = frappe.db.get_value('Customer', quotation.get("party_name"), 'default_price_list') - # else check for territory based price list + # check if default customer price list exists + if party_name: + selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) + + # check default price list in shopping cart if not selling_price_list: selling_price_list = cart_settings.price_list - party_name = quotation.get("party_name") if quotation else get_party().get("name") - - if not selling_price_list and party_name: - selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) - if quotation: quotation.selling_price_list = selling_price_list From 8a2c71935936cee537e242f5195c8567888fbd6a Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 10 Jun 2020 19:11:20 +0530 Subject: [PATCH 626/929] fix: only auto-set serial nos and batches if allowed in Stock Settings (v12) (#21779) * fix: only auto-set serial nos and batches if allowed in Stock Settings * fix: bug with setting disabled batch no in Pick List * Remove auto_set_batch_no Co-authored-by: Marica --- erpnext/stock/doctype/pick_list/pick_list.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 29c6c2ccbaa..63a59b87104 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -119,11 +119,13 @@ def get_items_with_location_and_quantity(item_doc, item_location_map): if item_location.serial_no: serial_nos = '\n'.join(item_location.serial_no[0: cint(stock_qty)]) + auto_set_serial_no = frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo") + locations.append(frappe._dict({ 'qty': qty, 'stock_qty': stock_qty, 'warehouse': item_location.warehouse, - 'serial_no': serial_nos, + 'serial_no': serial_nos if auto_set_serial_no else item_doc.serial_no, 'batch_no': item_location.batch_no })) @@ -206,6 +208,7 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re sle.batch_no = batch.name and sle.`item_code`=%(item_code)s and sle.`company` = %(company)s + and batch.disabled = 0 and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s {warehouse_condition} GROUP BY @@ -471,4 +474,4 @@ def update_common_item_properties(item, location): item.material_request = location.material_request item.serial_no = location.serial_no item.batch_no = location.batch_no - item.material_request_item = location.material_request_item \ No newline at end of file + item.material_request_item = location.material_request_item From 755b8490e7b2963e910b9522a964269c664e102b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 10 Jun 2020 22:15:27 +0530 Subject: [PATCH 627/929] fix: Update payment schedule based on payment terms (cherry picked from commit a0fd97f2ac1598ce7e3457aa4b30b19720906537) --- .../doctype/payment_entry/payment_entry.py | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index eae801368ca..a2e1a4a1612 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -318,7 +318,7 @@ class PaymentEntry(AccountsController): invoice_payment_amount_map.setdefault(key, 0.0) invoice_payment_amount_map[key] += reference.allocated_amount - if not invoice_paid_amount_map.get(reference.reference_name): + if not invoice_paid_amount_map.get(key): payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name}, fields=['paid_amount', 'payment_amount', 'payment_term']) for term in payment_schedule: @@ -331,12 +331,14 @@ class PaymentEntry(AccountsController): frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) else: - outstanding = invoice_paid_amount_map.get(key)['outstanding'] + outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding')) + if amount > outstanding: frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0])) - frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s - WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) + if amount and outstanding: + frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s + WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) def set_status(self): if self.docstatus == 2: @@ -1090,17 +1092,20 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount): references = [] for payment_term in payment_schedule: - references.append({ - 'reference_doctype': dt, - 'reference_name': dn, - 'bill_no': doc.get('bill_no'), - 'due_date': doc.get('due_date'), - 'total_amount': grand_total, - 'outstanding_amount': outstanding_amount, - 'payment_term': payment_term.payment_term, - 'allocated_amount': flt(payment_term.payment_amount - payment_term.paid_amount, + payment_term_outstanding = flt(payment_term.payment_amount - payment_term.paid_amount, payment_term.precision('payment_amount')) - }) + + if payment_term_outstanding: + references.append({ + 'reference_doctype': dt, + 'reference_name': dn, + 'bill_no': doc.get('bill_no'), + 'due_date': doc.get('due_date'), + 'total_amount': grand_total, + 'outstanding_amount': outstanding_amount, + 'payment_term': payment_term.payment_term, + 'allocated_amount': payment_term_outstanding + }) return references From 821606f00188d8207a9e14f00ef0909dfc3da350 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Jun 2020 13:19:53 +0530 Subject: [PATCH 628/929] fix: Handle unavailable Variants in Website --- erpnext/portal/product_configurator/utils.py | 3 +++ erpnext/shopping_cart/cart.py | 4 +++- erpnext/templates/generators/item/item_configure.js | 9 ++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 327a128068f..220bae888a7 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -1,5 +1,6 @@ import frappe import numpy as np +from frappe.utils import cint from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager def get_field_filter_data(): @@ -265,6 +266,8 @@ def get_next_attribute_and_values(item_code, selected_attributes): else: product_info = None + product_info["allow_items_not_in_stock"] = cint(frappe.db.get_singles_value("Shopping Cart Settings", "allow_items_not_in_stock")) + return { 'next_attribute': next_attribute, 'valid_options_for_attributes': valid_options_for_attributes, diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index e5b0abb1ed8..0fded2ec6f7 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -78,8 +78,10 @@ def place_order(): if is_stock_item: item_stock = get_qty_in_stock(item.item_code, "website_warehouse") + if not cint(item_stock.in_stock): + throw(_("{1} Not in Stock").format(item.item_code)) if item.qty > item_stock.stock_qty[0][0]: - throw(_("Only {0} in stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code)) + throw(_("Only {0} in Stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code)) sales_order.flags.ignore_permissions = True sales_order.insert() diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js index 5fd901169f0..163c955c566 100644 --- a/erpnext/templates/generators/item/item_configure.js +++ b/erpnext/templates/generators/item/item_configure.js @@ -193,14 +193,17 @@ class ItemConfigure { filtered_items_count === 1 ? filtered_items[0] : ''; + // Allow Add to Cart if adding out of stock items enabled in Shopping Cart else check stock. + const in_stock = product_info.allow_items_not_in_stock ? 1 : product_info.in_stock; + const add_to_cart = `${__('Add to cart')}`; + const product_action = in_stock ? add_to_cart : `${__('Not in Stock')}`; + const item_add_to_cart = one_item ? ` `: ''; From a965773c97c613f3b60436209169f96f12571263 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Jun 2020 16:39:03 +0530 Subject: [PATCH 629/929] fix: Don't prompt for Quality Inspection on Return Documents. --- erpnext/controllers/stock_controller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index d4b0a8d8466..29e0085dc07 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -19,7 +19,8 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass class StockController(AccountsController): def validate(self): super(StockController, self).validate() - self.validate_inspection() + if not self.is_return: + self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() From 5ee33bdbe954ec8e15f486825defb1243c0fd529 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Jun 2020 19:54:44 +0530 Subject: [PATCH 630/929] fix: Check for Company before rendering tree in Account Tree --- erpnext/accounts/doctype/account/account_tree.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index f62d07668de..28b090bdadb 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -14,6 +14,9 @@ frappe.treeview_settings["Account"] = { on_change: function() { var me = frappe.treeview_settings['Account'].treeview; var company = me.page.fields_dict.company.get_value(); + if (!company) { + frappe.throw(__("Please set a Company")); + } frappe.call({ method: "erpnext.accounts.doctype.account.account.get_root_company", args: { From d6cdc3378a519b750b9c0a355badf9752d7ca4af Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Fri, 12 Jun 2020 10:30:32 +0530 Subject: [PATCH 631/929] fix: add filter for cost center in expense table Added filter for cost center in expense table (company and group_node filter) --- erpnext/hr/doctype/expense_claim/expense_claim.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 3194007f231..88bd689d9de 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -154,6 +154,14 @@ frappe.ui.form.on("Expense Claim", { } }; }); + frm.set_query("cost_center", "expenses", function() { + return { + filters: { + "company": frm.doc.company, + "is_group": 0 + } + }; + }); frm.set_query("account_head", "taxes", function(doc) { return { filters: [ From 8ce5e04777af8ae7f2eb8d547f4a5005d2601a75 Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 12 Jun 2020 12:32:12 +0530 Subject: [PATCH 632/929] Update stock_controller.py --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 29e0085dc07..b238600881d 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -19,7 +19,7 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass class StockController(AccountsController): def validate(self): super(StockController, self).validate() - if not self.is_return: + if not self.get('is_return'): self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() From 37b614af0e5737fc9f85e449250da2c89eb9967d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 29 May 2020 21:55:38 +0530 Subject: [PATCH 633/929] fix: submitted sales order can be updated with no permission --- erpnext/controllers/accounts_controller.py | 46 ++++++++++++++----- erpnext/public/js/utils.js | 7 +++ .../doctype/sales_order/test_sales_order.py | 16 +++++++ 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e5073917b8d..fd74ee8d38a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1134,8 +1134,8 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.item_name = item.item_name child_item.description = item.description child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date + child_item.conversion_factor = flt(d.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") @@ -1154,8 +1154,8 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.item_name = item.item_name child_item.description = item.description child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date + child_item.conversion_factor = flt(d.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation return child_item @@ -1187,6 +1187,26 @@ def check_and_delete_children(parent, data): @frappe.whitelist() def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"): + def check_permissions(doc, perm_type='create'): + try: + doc.check_permission(perm_type) + except: + action = "add" if perm_type == 'create' else "update" + frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions")) + + def get_new_child_item(): + if parent_doctype == "Sales Order": + return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + if parent_doctype == "Purchase Order": + return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + + def validate_quantity(child_item, d): + if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): + frappe.throw(_("Cannot set quantity less than delivered quantity")) + + if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty): + frappe.throw(_("Cannot set quantity less than received quantity")) + data = json.loads(trans_items) sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation'] @@ -1198,20 +1218,19 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil new_child_flag = False if not d.get("docname"): new_child_flag = True - if parent_doctype == "Sales Order": - child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) - if parent_doctype == "Purchase Order": - child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + check_permissions(parent, 'create') + child_item = get_new_child_item() else: + check_permissions(parent, 'write') child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) - if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")): + + rate_unchanged = flt(child_item.get("rate")) == flt(d.get("rate")) + qty_unchanged = flt(child_item.get("qty")) == flt(d.get("qty")) + conversion_factor_unchanged = flt(child_item.get("conversion_factor")) == flt(d.get("conversion_factor")) + if rate_unchanged and qty_unchanged and conversion_factor_changed: continue - if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): - frappe.throw(_("Cannot set quantity less than delivered quantity")) - - if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty): - frappe.throw(_("Cannot set quantity less than received quantity")) + validate_quantity(child_item, d) child_item.qty = flt(d.get("qty")) precision = child_item.precision("rate") or 2 @@ -1221,6 +1240,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil .format(child_item.idx, child_item.item_code)) else: child_item.rate = flt(d.get("rate")) + + if d.get("conversion_factor"): + child_item.conversion_factor = flt(d.get('conversion_factor')) if flt(child_item.price_list_rate): if flt(child_item.rate) > flt(child_item.price_list_rate): diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index fb7471150cc..e0e90f84785 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -489,6 +489,12 @@ erpnext.utils.update_child_items = function(opts) { in_list_view: 1, label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date") }) + fields.splice(3, 0, { + fieldtype: 'Float', + fieldname: "conversion_factor", + in_list_view: 1, + label: __("UOM Conversion Factor") + }) } const dialog = new frappe.ui.Dialog({ @@ -536,6 +542,7 @@ erpnext.utils.update_child_items = function(opts) { "item_code": d.item_code, "delivery_date": d.delivery_date, "schedule_date": d.schedule_date, + "conversion_factor": d.conversion_factor, "qty": d.qty, "rate": d.rate, }); diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 9735c70830d..05e083483d3 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -399,6 +399,22 @@ class TestSalesOrder(unittest.TestCase): trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 2, 'docname': so.items[0].name}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + def test_update_child_qty_rate_perm(self): + so = make_sales_order(item_code= "_Test Item", qty=4) + + user = 'test@example.com' + test_user = frappe.get_doc('User', user) + test_user.add_roles("Accounts User") + frappe.set_user(user) + + # update qty + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + + # add new item + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + def test_warehouse_user(self): frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com") frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com") From 39e125b8a267bd3e85ebc4c01e108fb4ca1c3674 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 29 May 2020 21:59:01 +0530 Subject: [PATCH 634/929] fix: label --- erpnext/public/js/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index e0e90f84785..fe7cebf7d71 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -493,7 +493,7 @@ erpnext.utils.update_child_items = function(opts) { fieldtype: 'Float', fieldname: "conversion_factor", in_list_view: 1, - label: __("UOM Conversion Factor") + label: __("Conversion Factor") }) } From 9ee4841520bfdf36f5400cfabdfe2460a316cdb9 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 29 May 2020 22:21:50 +0530 Subject: [PATCH 635/929] fix: cannot update delivery date --- erpnext/controllers/accounts_controller.py | 24 ++++++++++++++++++---- erpnext/public/js/utils.js | 3 ++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index fd74ee8d38a..e567ee25bbf 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1224,10 +1224,20 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil check_permissions(parent, 'write') child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) - rate_unchanged = flt(child_item.get("rate")) == flt(d.get("rate")) - qty_unchanged = flt(child_item.get("qty")) == flt(d.get("qty")) - conversion_factor_unchanged = flt(child_item.get("conversion_factor")) == flt(d.get("conversion_factor")) - if rate_unchanged and qty_unchanged and conversion_factor_changed: + prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate")) + prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty")) + prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor")) + + if parent_doctype == 'Sales Order': + prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date") + elif parent_doctype == 'Purchase Order': + prev_date, new_date = child_item.get("schedule_date") == d.get("schedule_date") + + rate_unchanged = prev_rate == new_rate + qty_unchanged = prev_qty == prev_qty + conversion_factor_unchanged = prev_con_fac == new_con_fac + date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc + if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged: continue validate_quantity(child_item, d) @@ -1244,6 +1254,12 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if d.get("conversion_factor"): child_item.conversion_factor = flt(d.get('conversion_factor')) + if d.get("delivery_date") and parent_doctype == 'Sales Order': + child_item.delivery_date = d.get('delivery_date') + + if d.get("schedule_date") and parent_doctype == 'Purchase Order': + child_item.schedule_date = d.get('schedule_date') + if flt(child_item.price_list_rate): if flt(child_item.rate) > flt(child_item.price_list_rate): # if rate is greater than price_list_rate, set margin diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index fe7cebf7d71..9b1c94e5ba0 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -487,7 +487,8 @@ erpnext.utils.update_child_items = function(opts) { fieldtype: 'Date', fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date", in_list_view: 1, - label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date") + label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date"), + reqd: 1 }) fields.splice(3, 0, { fieldtype: 'Float', From 6a9f36829252b51721885052b5f909e48db6b483 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 29 May 2020 22:44:59 +0530 Subject: [PATCH 636/929] fix: unexpected removal of print hide field --- erpnext/selling/doctype/sales_order_item/sales_order_item.json | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 722a1adc2fa..909f609a6f1 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -210,6 +210,7 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", + "print_hide": 0, "reqd": 1 }, { From f6054a2de89fca1465f82521bcaa8627e659f931 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 12 Jun 2020 15:49:53 +0530 Subject: [PATCH 637/929] fix: Added PO test and conversion factor fix - Don't change conversion factor if stock uom and uom is the same - Added PO test - Added Accounts User basic role in PO - Minor fixes, wrong variables --- .../purchase_order/purchase_order.json | 8 +++++++- .../purchase_order/test_purchase_order.py | 20 +++++++++++++++++-- erpnext/controllers/accounts_controller.py | 15 ++++++++------ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 0ccdb25c32f..2018d068edf 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1056,7 +1056,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-04-17 13:04:28.185197", + "modified": "2020-06-12 14:08:11.777120", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", @@ -1100,6 +1100,12 @@ "read": 1, "role": "Purchase Manager", "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts User" } ], "search_fields": "status, transaction_date, supplier,grand_total", diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 2b6367b08d1..4a6e8642f8c 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -116,7 +116,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(po.get("items")[0].amount, 1400) self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3) - + def test_add_new_item_in_update_child_qty_rate(self): po = create_purchase_order(do_not_save=1) po.items[0].qty = 4 @@ -142,7 +142,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEquals(len(po.get('items')), 2) self.assertEqual(po.status, 'To Receive and Bill') - + def test_remove_item_in_update_child_qty_rate(self): po = create_purchase_order(do_not_save=1) po.items[0].qty = 4 @@ -183,6 +183,22 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEquals(len(po.get('items')), 1) self.assertEqual(po.status, 'To Receive and Bill') + def test_update_child_qty_rate_perm(self): + po = create_purchase_order(item_code= "_Test Item", qty=4) + + user = 'test@example.com' + test_user = frappe.get_doc('User', user) + test_user.add_roles("Accounts User") + frappe.set_user(user) + + # update qty + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.items[0].name}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) + + # add new item + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) + def test_update_qty(self): po = create_purchase_order() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e567ee25bbf..df0715eae85 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1134,7 +1134,7 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.item_name = item.item_name child_item.description = item.description child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date - child_item.conversion_factor = flt(d.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: @@ -1154,7 +1154,7 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.item_name = item.item_name child_item.description = item.description child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date - child_item.conversion_factor = flt(d.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation @@ -1234,7 +1234,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil prev_date, new_date = child_item.get("schedule_date") == d.get("schedule_date") rate_unchanged = prev_rate == new_rate - qty_unchanged = prev_qty == prev_qty + qty_unchanged = prev_qty == new_qty conversion_factor_unchanged = prev_con_fac == new_con_fac date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged: @@ -1250,13 +1250,16 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil .format(child_item.idx, child_item.item_code)) else: child_item.rate = flt(d.get("rate")) - + if d.get("conversion_factor"): - child_item.conversion_factor = flt(d.get('conversion_factor')) + if child_item.stock_uom == child_item.uom: + child_item.conversion_factor = 1 + else: + child_item.conversion_factor = flt(d.get('conversion_factor')) if d.get("delivery_date") and parent_doctype == 'Sales Order': child_item.delivery_date = d.get('delivery_date') - + if d.get("schedule_date") and parent_doctype == 'Purchase Order': child_item.schedule_date = d.get('schedule_date') From 9319153ab365296d5ec9a5a014218deb665468ad Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Fri, 12 Jun 2020 17:04:25 +0530 Subject: [PATCH 638/929] fix: Child is shown in Parent process if added from tree view --- .../doctype/quality_procedure/quality_procedure.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index b4de03e1e4f..3ce5ea015f5 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -10,7 +10,7 @@ from frappe import _ class QualityProcedure(NestedSet): nsm_parent_field = 'parent_quality_procedure' - def before_save(self): + def on_save(self): for process in self.processes: if process.procedure: doc = frappe.get_doc("Quality Procedure", process.procedure) @@ -23,6 +23,11 @@ class QualityProcedure(NestedSet): def after_insert(self): self.set_parent() + #if Child is Added through Tree View. + if self.parent_quality_procedure: + parent_quality_procedure = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) + parent_quality_procedure.append("processes", {"procedure": self.name}) + parent_quality_procedure.save() def on_trash(self): if self.parent_quality_procedure: From 486be1049c1febcaac7d4814c8f79c8a66ac80bc Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Sat, 13 Jun 2020 01:18:29 +0530 Subject: [PATCH 639/929] fix(HR): Rename due_advance_amount field in Employee Advance for v12 (#22216) * added patch for moving data * changed fieldname and references for easier debugging * style: Added EOF * reload doc for patch --- .../employee_advance/employee_advance.js | 4 +-- .../employee_advance/employee_advance.json | 26 ++++++++++++------- .../employee_advance/employee_advance.py | 2 +- erpnext/patches.txt | 2 ++ ...ve_due_advance_amount_to_pending_amount.py | 9 +++++++ 5 files changed, 30 insertions(+), 13 deletions(-) create mode 100644 erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js index 389660387b7..70bf20114a4 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.js +++ b/erpnext/hr/doctype/employee_advance/employee_advance.js @@ -113,13 +113,13 @@ frappe.ui.form.on('Employee Advance', { employee: function (frm) { if (frm.doc.employee) { return frappe.call({ - method: "erpnext.hr.doctype.employee_advance.employee_advance.get_due_advance_amount", + method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount", args: { "employee": frm.doc.employee, "posting_date": frm.doc.posting_date }, callback: function(r) { - frm.set_value("due_advance_amount",r.message); + frm.set_value("pending_amount",r.message); } }); } diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json index d233a2bb936..435d160f33a 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.json +++ b/erpnext/hr/doctype/employee_advance/employee_advance.json @@ -18,7 +18,7 @@ "column_break_11", "advance_amount", "paid_amount", - "due_advance_amount", + "pending_amount", "claimed_amount", "return_amount", "section_break_7", @@ -101,14 +101,6 @@ "options": "Company:company:default_currency", "read_only": 1 }, - { - "depends_on": "eval:cur_frm.doc.employee", - "fieldname": "due_advance_amount", - "fieldtype": "Currency", - "label": "Due Advance Amount", - "options": "Company:company:default_currency", - "read_only": 1 - }, { "fieldname": "claimed_amount", "fieldtype": "Currency", @@ -169,11 +161,25 @@ "label": "Returned Amount", "options": "Company:company:default_currency", "read_only": 1 + }, + { + "default": "0", + "fieldname": "repay_unclaimed_amount_from_salary", + "fieldtype": "Check", + "label": "Repay unclaimed amount from salary" + }, + { + "depends_on": "eval:cur_frm.doc.employee", + "fieldname": "pending_amount", + "fieldtype": "Currency", + "label": "Pending Amount", + "options": "Company:company:default_currency", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2019-12-15 19:04:07.044505", + "modified": "2020-06-12 12:42:39.833818", "modified_by": "Administrator", "module": "HR", "name": "Employee Advance", diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index f0663aefa83..30a2a896dc6 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -95,7 +95,7 @@ class EmployeeAdvance(Document): frappe.db.set_value("Employee Advance", self.name, "status", self.status) @frappe.whitelist() -def get_due_advance_amount(employee, posting_date): +def get_pending_amount(employee, posting_date): employee_due_amount = frappe.get_all("Employee Advance", \ filters = {"employee":employee, "docstatus":1, "posting_date":("<=", posting_date)}, \ fields = ["advance_amount", "paid_amount"]) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index fb83fadb71d..34faea36eb2 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -669,3 +669,5 @@ erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions +execute:frappe.reload_doc("HR", "doctype", "Employee Advance") +erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount diff --git a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py new file mode 100644 index 00000000000..f1ffaf9d2d4 --- /dev/null +++ b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py @@ -0,0 +1,9 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + ''' Move from due_advance_amount to pending_amount ''' + frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') From 1222fb074fccd4676ac70cdb307b05cd48ea70bf Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 13 Jun 2020 19:16:37 +0530 Subject: [PATCH 640/929] fix: Data not appearing properly for some fiscal_year in financial statemets --- erpnext/accounts/report/financial_statements.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 48d1cde4550..7e96b9e237b 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -50,9 +50,8 @@ def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_v to_date = add_months(start_date, months_to_add) start_date = to_date - if to_date == get_first_day(to_date): - # if to_date is the first day, get the last day of previous month - to_date = add_days(to_date, -1) + # Subtract one day from to_date, as it may be first day in next fiscal year or month + to_date = add_days(to_date, -1) if to_date <= year_end_date: # the normal case From e4cd23746bc0068ebc3e8e693bdf0ea9313d580e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 14 Jun 2020 13:18:08 +0530 Subject: [PATCH 641/929] fix: Consider Overseas category in RCM --- .../doctype/gstr_3b_report/gstr_3b_report.py | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 2cdf29f4fee..626a88acec7 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -158,7 +158,7 @@ class GSTR3BReport(Document): self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_det", ["Registered Regular"]) self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_zero", ["SEZ", "Deemed Export", "Overseas"]) - self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Unregistered"], reverse_charge="Y") + self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Unregistered", "Overseas"], reverse_charge="Y") self.report_dict["sup_details"]["osup_nil_exmp"]["txval"] = flt(self.get_nil_rated_supply_value(), 2) self.set_itc_details(itc_details) @@ -192,32 +192,27 @@ class GSTR3BReport(Document): for d in self.report_dict["itc_elg"]["itc_avl"]: itc_type = itc_type_map.get(d["ty"]) - gst_category = "Registered Regular" + gst_category = ["Registered Regular"] if d["ty"] == 'ISRC': reverse_charge = "Y" itc_type = 'All Other ITC' - gst_category = 'Unregistered' + gst_category = ['Unregistered', 'Overseas'] else: reverse_charge = "N" for account_head in self.account_heads: - d["iamt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('igst_account')), {}).get("amount"), 2) - d["camt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('cgst_account')), {}).get("amount"), 2) - d["samt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('sgst_account')), {}).get("amount"), 2) - d["csamt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('cess_account')), {}).get("amount"), 2) + for category in gst_category: + for key in [['iamt', 'igst_account'], ['camt', 'cgst_account'], ['samt', 'sgst_account'], ['csamt', 'cess_account']]: + d[key[0]] += flt(itc_details.get((category, itc_type, reverse_charge, account_head.get(key[1])), {}).get("amount"), 2) - net_itc["iamt"] += flt(d["iamt"], 2) - net_itc["camt"] += flt(d["camt"], 2) - net_itc["samt"] += flt(d["samt"], 2) - net_itc["csamt"] += flt(d["csamt"], 2) + for key in ['iamt', 'camt', 'samt', 'csamt']: + net_itc[key] += flt(d[key], 2) for account_head in self.account_heads: itc_inelg = self.report_dict["itc_elg"]["itc_inelg"][1] - itc_inelg["iamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("igst_account")), {}).get("amount"), 2) - itc_inelg["camt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cgst_account")), {}).get("amount"), 2) - itc_inelg["samt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("sgst_account")), {}).get("amount"), 2) - itc_inelg["csamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cess_account")), {}).get("amount"), 2) + for key in [['iamt', 'igst_account'], ['camt', 'cgst_account'], ['samt', 'sgst_account'], ['csamt', 'cess_account']]: + itc_inelg[key[0]] = flt(itc_details.get(("Ineligible", "N", account_head.get(key[1])), {}).get("amount"), 2) def prepare_data(self, doctype, tax_details, supply_type, supply_category, gst_category_list, reverse_charge="N"): From 5b11d18d8686456a905f4c83f4d33d95bc6eeb9e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 14 Jun 2020 14:06:12 +0530 Subject: [PATCH 642/929] fix: item none not found while making sales invoice using opening invoice creation tool --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index fa175fc816e..ce26a258fb6 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -552,6 +552,8 @@ class SalesInvoice(SellingController): continue for d in self.get('items'): + if not d.item_code: continue + is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item') if (d.item_code and is_stock_item ==1 and not d.get(key.lower().replace(' ', '_')) and not self.get(value[1])): msgprint(_("{0} is mandatory for Item {1}").format(key, d.item_code), raise_exception=1) @@ -574,14 +576,14 @@ class SalesInvoice(SellingController): def validate_item_code(self): for d in self.get('items'): - if not d.item_code: + if not d.item_code and self.is_opening == "No": msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True) def validate_warehouse(self): super(SalesInvoice, self).validate_warehouse() for d in self.get_item_list(): - if not d.warehouse and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): + if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code)) def validate_delivery_note(self): From 0a31e2ea924a0a3587fb39786787116aadfa8442 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 15 Jun 2020 12:00:16 +0530 Subject: [PATCH 643/929] fix: Fetch cart setting from argument --- erpnext/portal/product_configurator/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 220bae888a7..69ff1dcccaa 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -266,7 +266,7 @@ def get_next_attribute_and_values(item_code, selected_attributes): else: product_info = None - product_info["allow_items_not_in_stock"] = cint(frappe.db.get_singles_value("Shopping Cart Settings", "allow_items_not_in_stock")) + product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock) return { 'next_attribute': next_attribute, From 713cbcea44839f2f777f75fa3387dd76dfb5f7c6 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 15 Jun 2020 11:32:42 +0530 Subject: [PATCH 644/929] fix: Travis --- erpnext/buying/doctype/purchase_order/test_purchase_order.py | 1 + erpnext/controllers/accounts_controller.py | 4 ++-- erpnext/selling/doctype/sales_order/test_sales_order.py | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 4a6e8642f8c..075db6e46ba 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -198,6 +198,7 @@ class TestPurchaseOrder(unittest.TestCase): # add new item trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) + frappe.set_user("Administrator") def test_update_qty(self): po = create_purchase_order() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index df0715eae85..a4bed51f479 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1178,7 +1178,7 @@ def check_and_delete_children(parent, data): if parent.doctype == "Purchase Order" and flt(d.received_qty): frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(d.idx, d.item_code)) - + if flt(d.billed_amt): frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(d.idx, d.item_code)) @@ -1231,7 +1231,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if parent_doctype == 'Sales Order': prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date") elif parent_doctype == 'Purchase Order': - prev_date, new_date = child_item.get("schedule_date") == d.get("schedule_date") + prev_date, new_date = child_item.get("schedule_date"), d.get("schedule_date") rate_unchanged = prev_rate == new_rate qty_unchanged = prev_qty == new_qty diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 05e083483d3..6a6d7c96a22 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -334,7 +334,7 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(so.get("items")[-1].qty, 7) self.assertEqual(so.get("items")[-1].amount, 1400) self.assertEqual(so.status, 'To Deliver and Bill') - + def test_remove_item_in_update_child_qty_rate(self): so = make_sales_order(**{ "item_list": [{ @@ -372,7 +372,7 @@ class TestSalesOrder(unittest.TestCase): "docname": so.get("items")[0].name }]) update_child_qty_rate('Sales Order', trans_item, so.name) - + so.reload() self.assertEqual(len(so.get("items")), 1) self.assertEqual(so.status, 'To Deliver and Bill') @@ -414,6 +414,7 @@ class TestSalesOrder(unittest.TestCase): # add new item trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + frappe.set_user("Administrator") def test_warehouse_user(self): frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com") From 84f44588d8f3d053304cfc7fcf65aa197b3c298d Mon Sep 17 00:00:00 2001 From: P-Froggy <60393001+P-Froggy@users.noreply.github.com> Date: Mon, 15 Jun 2020 10:10:54 +0200 Subject: [PATCH 645/929] fix: Validation of Purchase Order against Material Request missing (#22224) Validation of Purchase Order and Purchase Order Item against the linked Material Request Item was missing. This way, it was possible to exchange items while making a purchase order and still fulfill the material request, even though the items did not match. Co-authored-by: Marica --- erpnext/buying/doctype/purchase_order/purchase_order.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index f62df20ae1a..c7efb8a1a17 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -71,6 +71,15 @@ class PurchaseOrder(BuyingController): "compare_fields": [["project", "="], ["item_code", "="], ["uom", "="], ["conversion_factor", "="]], "is_child_table": True + }, + "Material Request": { + "ref_dn_field": "material_request", + "compare_fields": [["company", "="]], + }, + "Material Request Item": { + "ref_dn_field": "material_request_item", + "compare_fields": [["project", "="], ["item_code", "="]], + "is_child_table": True } }) From b819d8cdb6383a8e0c980efb93496b7b2bf7efc3 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 15 Jun 2020 14:40:39 +0530 Subject: [PATCH 646/929] fix: Wrong key sent to gte_valuation_rate --- erpnext/controllers/buying_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 70f9033f431..0973f165232 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -340,7 +340,7 @@ class BuyingController(StockController): }) if not rm.rate: - rm.rate = get_valuation_rate(raw_material_data.item_code, self.supplier_warehouse, + rm.rate = get_valuation_rate(raw_material_data.rm_item_code, self.supplier_warehouse, self.doctype, self.name, currency=self.company_currency, company=self.company) rm.amount = qty * flt(rm.rate) From 1e76bdcc3cb68a0ba2b04d65592b992bf844f12f Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 15 Jun 2020 17:38:47 +0530 Subject: [PATCH 647/929] feat: Multi UOM support in Request for Quotation --- .../request_for_quotation.py | 2 + .../test_request_for_quotation.py | 48 ++++++++++++++----- .../request_for_quotation_item.json | 43 ++++++++++++++--- erpnext/patches.txt | 1 + erpnext/patches/v12_0/set_multi_uom_in_rfq.py | 29 +++++++++++ 5 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 erpnext/patches/v12_0/set_multi_uom_in_rfq.py diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index 95db33b0f8f..dfdb487f9e0 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -25,6 +25,7 @@ class RequestforQuotation(BuyingController): self.validate_duplicate_supplier() self.validate_supplier_list() validate_for_items(self) + super(RequestforQuotation, self).set_qty_as_per_stock_uom() self.update_email_id() def validate_duplicate_supplier(self): @@ -278,6 +279,7 @@ def create_rfq_items(sq_doc, supplier, data): "description": data.description, "qty": data.qty, "rate": data.rate, + "conversion_factor": data.conversion_factor if data.conversion_factor else None, "supplier_part_no": frappe.db.get_value("Item Supplier", {'parent': data.item_code, 'supplier': supplier}, "supplier_part_no"), "warehouse": data.warehouse or '', "request_for_quotation_item": data.name, diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py index dbd9f022789..3de9526c4f2 100644 --- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py @@ -6,12 +6,14 @@ from __future__ import unicode_literals import unittest import frappe -from erpnext.templates.pages.rfq import check_supplier_has_docname_access from frappe.utils import nowdate +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.templates.pages.rfq import check_supplier_has_docname_access +from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation +from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation class TestRequestforQuotation(unittest.TestCase): def test_quote_status(self): - from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation rfq = make_request_for_quotation() self.assertEqual(rfq.get('suppliers')[0].quote_status, 'Pending') @@ -31,7 +33,6 @@ class TestRequestforQuotation(unittest.TestCase): self.assertEqual(rfq.get('suppliers')[1].quote_status, 'No Quote') def test_make_supplier_quotation(self): - from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation rfq = make_request_for_quotation() sq = make_supplier_quotation(rfq.name, rfq.get('suppliers')[0].supplier) @@ -51,15 +52,13 @@ class TestRequestforQuotation(unittest.TestCase): self.assertEqual(sq1.get('items')[0].qty, 5) def test_make_supplier_quotation_with_special_characters(self): - from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation - frappe.delete_doc_if_exists("Supplier", "_Test Supplier '1", force=1) supplier = frappe.new_doc("Supplier") supplier.supplier_name = "_Test Supplier '1" supplier.supplier_group = "_Test Supplier Group" supplier.insert() - rfq = make_request_for_quotation(supplier_wt_appos) + rfq = make_request_for_quotation(supplier_data=supplier_wt_appos) sq = make_supplier_quotation(rfq.name, supplier_wt_appos[0].get("supplier")) sq.submit() @@ -76,7 +75,6 @@ class TestRequestforQuotation(unittest.TestCase): frappe.form_dict.name = None def test_make_supplier_quotation_from_portal(self): - from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation rfq = make_request_for_quotation() rfq.get('items')[0].rate = 100 rfq.supplier = rfq.suppliers[0].supplier @@ -90,12 +88,34 @@ class TestRequestforQuotation(unittest.TestCase): self.assertEqual(supplier_quotation_doc.get('items')[0].qty, 5) self.assertEqual(supplier_quotation_doc.get('items')[0].amount, 500) + def test_make_multi_uom_supplier_quotation(self): + item_code = "_Test Multi UOM RFQ Item" + if not frappe.db.exists('Item', item_code): + item = make_item(item_code, {'stock_uom': '_Test UOM'}) + row = item.append('uoms', { + 'uom': 'Kg', + 'conversion_factor': 2 + }) + row.db_update() -def make_request_for_quotation(supplier_data=None): + rfq = make_request_for_quotation(item_code="_Test Multi UOM RFQ Item", uom="Kg", conversion_factor=2) + rfq.get('items')[0].rate = 100 + rfq.supplier = rfq.suppliers[0].supplier + + self.assertEqual(rfq.items[0].stock_qty, 10) + + supplier_quotation_name = create_supplier_quotation(rfq) + supplier_quotation = frappe.get_doc('Supplier Quotation', supplier_quotation_name) + + self.assertEqual(supplier_quotation.items[0].qty, 5) + self.assertEqual(supplier_quotation.items[0].stock_qty, 10) + +def make_request_for_quotation(**args): """ :param supplier_data: List containing supplier data """ - supplier_data = supplier_data if supplier_data else get_supplier_data() + args = frappe._dict(args) + supplier_data = args.get("supplier_data") if args.get("supplier_data") else get_supplier_data() rfq = frappe.new_doc('Request for Quotation') rfq.transaction_date = nowdate() rfq.status = 'Draft' @@ -106,11 +126,13 @@ def make_request_for_quotation(supplier_data=None): rfq.append('suppliers', data) rfq.append("items", { - "item_code": "_Test Item", + "item_code": args.item_code or "_Test Item", "description": "_Test Item", - "uom": "_Test UOM", - "qty": 5, - "warehouse": "_Test Warehouse - _TC", + "uom": args.uom or "_Test UOM", + "stock_uom": args.stock_uom or "_Test UOM", + "qty": args.qty or 5, + "conversion_factor": args.conversion_factor or 1.0, + "warehouse": args.warehouse or "_Test Warehouse - _TC", "schedule_date": nowdate() }) diff --git a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json index 0159df962ec..408f49f5233 100644 --- a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json +++ b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2016-02-25 08:04:02.452958", "doctype": "DocType", @@ -9,6 +10,7 @@ "supplier_part_no", "column_break_3", "item_name", + "schedule_date", "section_break_5", "description", "item_group", @@ -18,9 +20,11 @@ "image_view", "quantity", "qty", + "stock_uom", "col_break2", - "schedule_date", "uom", + "conversion_factor", + "stock_qty", "warehouse_and_reference", "warehouse", "project_name", @@ -33,7 +37,7 @@ "fields": [ { "bold": 1, - "columns": 3, + "columns": 2, "fieldname": "item_code", "fieldtype": "Link", "in_list_view": 1, @@ -98,7 +102,7 @@ { "fieldname": "quantity", "fieldtype": "Section Break", - "label": "Quantity" + "label": "Quantity & Stock" }, { "bold": 1, @@ -129,12 +133,12 @@ { "fieldname": "uom", "fieldtype": "Link", + "in_list_view": 1, "label": "UOM", "oldfieldname": "uom", "oldfieldtype": "Link", "options": "UOM", "print_width": "100px", - "read_only": 1, "reqd": 1, "width": "100px" }, @@ -144,7 +148,7 @@ "label": "Warehouse and Reference" }, { - "columns": 3, + "columns": 2, "fieldname": "warehouse", "fieldtype": "Link", "in_list_view": 1, @@ -202,6 +206,7 @@ }, { "allow_on_submit": 1, + "default": "0", "fieldname": "page_break", "fieldtype": "Check", "label": "Page Break", @@ -219,10 +224,36 @@ { "fieldname": "section_break_23", "fieldtype": "Section Break" + }, + { + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "conversion_factor", + "fieldtype": "Float", + "label": "UOM Conversion Factor", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "stock_qty", + "fieldtype": "Float", + "label": "Qty as per Stock UOM", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "istable": 1, - "modified": "2019-05-01 17:50:23.703801", + "links": [], + "modified": "2020-06-12 19:10:36.333441", "modified_by": "Administrator", "module": "Buying", "name": "Request for Quotation Item", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 34faea36eb2..0f865760ca9 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -671,3 +671,4 @@ erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions execute:frappe.reload_doc("HR", "doctype", "Employee Advance") erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount +erpnext.patches.v12_0.set_multi_uom_in_rfq diff --git a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py new file mode 100644 index 00000000000..50725bb69dd --- /dev/null +++ b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py @@ -0,0 +1,29 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import flt +from erpnext.stock.get_item_details import get_conversion_factor + +def execute(): + frappe.reload_doc('buying', 'doctype', 'request_for_quotation_item') + + for rfq_item in frappe.db.sql("""SELECT name, item_code, uom, qty FROM `tabRequest for Quotation Item` WHERE docstatus<2""", as_dict=1): + item_code, uom, qty = rfq_item.get("item_code"), rfq_item.get("uom"), rfq_item.get("qty") + conversion_factor = get_conversion_factor(item_code, uom).get("conversion_factor") or 1.0 + + filters = { + "name" : rfq_item.get("name"), + "stock_uom" : frappe.db.get_value("Item", item_code, "stock_uom"), + "conversion_factor" : conversion_factor, + "stock_qty" : flt(qty) * flt(conversion_factor) + } + + frappe.db.sql("""UPDATE `tabRequest for Quotation Item` + SET + stock_uom= %(stock_uom)s, + conversion_factor = %(conversion_factor)s, + stock_qty = %(stock_qty)s + WHERE + name = %(name)s""", filters) \ No newline at end of file From e4834d2a3738f2be1d24b33d3b3ef2bb9d789a72 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 15 Jun 2020 16:57:20 +0530 Subject: [PATCH 648/929] fix: travis --- .../loyalty_program/test_loyalty_program.py | 1 + .../test_procurement_tracker.py | 19 +++++++++++-------- ...ve_due_advance_amount_to_pending_amount.py | 4 +++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py index 341884c1901..20fbde19da1 100644 --- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py @@ -68,6 +68,7 @@ class TestLoyaltyProgram(unittest.TestCase): lpe = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer}) + customer.load_from_db() self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program) self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier) self.assertEqual(lpe.loyalty_points, earned_points) diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index bebf0ccec56..c7204a1f341 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -15,7 +15,7 @@ class TestProcurementTracker(unittest.TestCase): def test_result_for_procurement_tracker(self): filters = { 'company': '_Test Procurement Company', - 'cost_center': '_Test Cost Center - _TC' + 'cost_center': 'Main - _TPC' } expected_data = self.generate_expected_data() report = execute(filters) @@ -33,24 +33,27 @@ class TestProcurementTracker(unittest.TestCase): country="Pakistan" )).insert() warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company") - mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse) + mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC") po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" - po.get("items")[0].cost_center = "_Test Cost Center - _TC" + po.get("items")[0].cost_center = "Main - _TPC" po.submit() pr = make_purchase_receipt(po.name) + pr.get("items")[0].cost_center = "Main - _TPC" pr.submit() frappe.db.commit() date_obj = datetime.date(datetime.now()) + po.load_from_db() + expected_data = { "material_request_date": date_obj, - "cost_center": "_Test Cost Center - _TC", + "cost_center": "Main - _TPC", "project": None, "requesting_site": "_Test Procurement Warehouse - _TPC", "requestor": "Administrator", "material_request_no": mr.name, - "description": '_Test Item 1', + "item_code": '_Test Item', "quantity": 10.0, "unit_of_measurement": "_Test UOM", "status": "To Bill", @@ -58,9 +61,9 @@ class TestProcurementTracker(unittest.TestCase): "purchase_order": po.name, "supplier": "_Test Supplier", "estimated_cost": 0.0, - "actual_cost": None, - "purchase_order_amt": 5000.0, - "purchase_order_amt_in_company_currency": 300000.0, + "actual_cost": 0.0, + "purchase_order_amt": po.net_total, + "purchase_order_amt_in_company_currency": po.base_net_total, "expected_delivery_date": date_obj, "actual_delivery_date": date_obj } diff --git a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py index f1ffaf9d2d4..6013eaa29c6 100644 --- a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py +++ b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py @@ -6,4 +6,6 @@ import frappe def execute(): ''' Move from due_advance_amount to pending_amount ''' - frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') + + if frappe.db.has_column("Employee Advance", "due_advance_amount"): + frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') From 03f688acd9705eefb4a07bfe6b0eec456261d88f Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 17 Jun 2020 09:36:23 +0530 Subject: [PATCH 649/929] fix: pos, serial no popup comin two times (#22268) --- erpnext/public/js/controllers/transaction.js | 3 +-- erpnext/selling/sales_common.js | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 0d3bbd658f7..445d683788e 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -4,7 +4,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ setup: function() { this._super(); - frappe.flags.hide_serial_batch_dialog = true; frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); var has_margin_field = frappe.meta.has_field(cdt, 'margin_type'); @@ -539,7 +538,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if (show_batch_dialog) return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) .then((r) => { - if(r.message && + if(r.message && !frappe.flags.hide_serial_batch_dialog && (r.message.has_batch_no || r.message.has_serial_no)) { frappe.flags.hide_serial_batch_dialog = false; } diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 4a7dd5ad9b4..99c4e62fdbe 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -229,7 +229,10 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ var me = this; var item = frappe.get_doc(cdt, cdn); - if (item.serial_no && item.qty === item.serial_no.split(`\n`).length) { + let serial_no_count = item.serial_no + ? item.serial_no.split(`\n`).filter(d => d).length : 0; + + if (item.serial_no && item.qty === serial_no_count) { return; } From fd3ff6be18b0bd5accc88aea22e3adfe48e62aff Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 17 Jun 2020 09:37:41 +0530 Subject: [PATCH 650/929] refactor: hide company currency fields in the routing (#22267) --- erpnext/manufacturing/doctype/bom/bom.py | 1 + .../doctype/bom_operation/bom_operation.json | 8 +++++--- erpnext/manufacturing/doctype/routing/routing.js | 1 - 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 41881efec2d..067a6f7674d 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -108,6 +108,7 @@ class BOM(WebsiteGenerator): "description": d.description, "time_in_mins": d.time_in_mins, "batch_size": d.batch_size, + "operating_cost": d.operating_cost, "idx": d.idx }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json index 3ca851d783b..0350e2cb374 100644 --- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json +++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json @@ -78,6 +78,7 @@ "read_only": 1 }, { + "depends_on": "eval:parent.doctype == 'BOM'", "fieldname": "base_hour_rate", "fieldtype": "Currency", "label": "Base Hour Rate(Company Currency)", @@ -87,6 +88,7 @@ }, { "default": "5", + "depends_on": "eval:parent.doctype == 'BOM'", "fieldname": "base_operating_cost", "fieldtype": "Currency", "label": "Operating Cost(Company Currency)", @@ -108,12 +110,12 @@ ], "idx": 1, "istable": 1, - "modified": "2019-07-16 22:35:55.374037", - "modified_by": "govindsmenokee@gmail.com", + "modified": "2020-06-16 17:01:11.128420", + "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Operation", "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC" -} +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/routing/routing.js b/erpnext/manufacturing/doctype/routing/routing.js index 6cfd0bae5b5..d7589fa3907 100644 --- a/erpnext/manufacturing/doctype/routing/routing.js +++ b/erpnext/manufacturing/doctype/routing/routing.js @@ -44,7 +44,6 @@ frappe.ui.form.on('BOM Operation', { name: d.workstation }, callback: function (data) { - frappe.model.set_value(d.doctype, d.name, "base_hour_rate", data.message.hour_rate); frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate); frm.events.calculate_operating_cost(frm, d); } From 956368e457c39e5269b37fb380a6a85eeeafbed7 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 17 Jun 2020 09:37:41 +0530 Subject: [PATCH 651/929] refactor: hide company currency fields in the routing (#22267) (cherry picked from commit fd3ff6be18b0bd5accc88aea22e3adfe48e62aff) --- erpnext/manufacturing/doctype/bom/bom.py | 1 + .../doctype/bom_operation/bom_operation.json | 8 +++++--- erpnext/manufacturing/doctype/routing/routing.js | 1 - 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 41881efec2d..067a6f7674d 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -108,6 +108,7 @@ class BOM(WebsiteGenerator): "description": d.description, "time_in_mins": d.time_in_mins, "batch_size": d.batch_size, + "operating_cost": d.operating_cost, "idx": d.idx }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json index 3ca851d783b..0350e2cb374 100644 --- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json +++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json @@ -78,6 +78,7 @@ "read_only": 1 }, { + "depends_on": "eval:parent.doctype == 'BOM'", "fieldname": "base_hour_rate", "fieldtype": "Currency", "label": "Base Hour Rate(Company Currency)", @@ -87,6 +88,7 @@ }, { "default": "5", + "depends_on": "eval:parent.doctype == 'BOM'", "fieldname": "base_operating_cost", "fieldtype": "Currency", "label": "Operating Cost(Company Currency)", @@ -108,12 +110,12 @@ ], "idx": 1, "istable": 1, - "modified": "2019-07-16 22:35:55.374037", - "modified_by": "govindsmenokee@gmail.com", + "modified": "2020-06-16 17:01:11.128420", + "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Operation", "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC" -} +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/routing/routing.js b/erpnext/manufacturing/doctype/routing/routing.js index 6cfd0bae5b5..d7589fa3907 100644 --- a/erpnext/manufacturing/doctype/routing/routing.js +++ b/erpnext/manufacturing/doctype/routing/routing.js @@ -44,7 +44,6 @@ frappe.ui.form.on('BOM Operation', { name: d.workstation }, callback: function (data) { - frappe.model.set_value(d.doctype, d.name, "base_hour_rate", data.message.hour_rate); frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate); frm.events.calculate_operating_cost(frm, d); } From 9c55efa8c6ef278bb0d976d9d126c28ba9291328 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 17 Jun 2020 09:36:23 +0530 Subject: [PATCH 652/929] fix: pos, serial no popup comin two times (#22268) (cherry picked from commit 03f688acd9705eefb4a07bfe6b0eec456261d88f) --- erpnext/public/js/controllers/transaction.js | 3 +-- erpnext/selling/sales_common.js | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 0d3bbd658f7..445d683788e 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -4,7 +4,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ setup: function() { this._super(); - frappe.flags.hide_serial_batch_dialog = true; frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); var has_margin_field = frappe.meta.has_field(cdt, 'margin_type'); @@ -539,7 +538,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if (show_batch_dialog) return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) .then((r) => { - if(r.message && + if(r.message && !frappe.flags.hide_serial_batch_dialog && (r.message.has_batch_no || r.message.has_serial_no)) { frappe.flags.hide_serial_batch_dialog = false; } diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 4a7dd5ad9b4..99c4e62fdbe 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -229,7 +229,10 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ var me = this; var item = frappe.get_doc(cdt, cdn); - if (item.serial_no && item.qty === item.serial_no.split(`\n`).length) { + let serial_no_count = item.serial_no + ? item.serial_no.split(`\n`).filter(d => d).length : 0; + + if (item.serial_no && item.qty === serial_no_count) { return; } From 5e68fdbaee00ce61aa90f9b671dd9ddaf324c626 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 15 Jun 2020 16:57:20 +0530 Subject: [PATCH 653/929] fix: travis (cherry picked from commit e4834d2a3738f2be1d24b33d3b3ef2bb9d789a72) --- .../loyalty_program/test_loyalty_program.py | 1 + .../test_procurement_tracker.py | 19 +++++++++++-------- ...ve_due_advance_amount_to_pending_amount.py | 4 +++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py index 341884c1901..20fbde19da1 100644 --- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py @@ -68,6 +68,7 @@ class TestLoyaltyProgram(unittest.TestCase): lpe = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer}) + customer.load_from_db() self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program) self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier) self.assertEqual(lpe.loyalty_points, earned_points) diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index bebf0ccec56..c7204a1f341 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -15,7 +15,7 @@ class TestProcurementTracker(unittest.TestCase): def test_result_for_procurement_tracker(self): filters = { 'company': '_Test Procurement Company', - 'cost_center': '_Test Cost Center - _TC' + 'cost_center': 'Main - _TPC' } expected_data = self.generate_expected_data() report = execute(filters) @@ -33,24 +33,27 @@ class TestProcurementTracker(unittest.TestCase): country="Pakistan" )).insert() warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company") - mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse) + mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC") po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" - po.get("items")[0].cost_center = "_Test Cost Center - _TC" + po.get("items")[0].cost_center = "Main - _TPC" po.submit() pr = make_purchase_receipt(po.name) + pr.get("items")[0].cost_center = "Main - _TPC" pr.submit() frappe.db.commit() date_obj = datetime.date(datetime.now()) + po.load_from_db() + expected_data = { "material_request_date": date_obj, - "cost_center": "_Test Cost Center - _TC", + "cost_center": "Main - _TPC", "project": None, "requesting_site": "_Test Procurement Warehouse - _TPC", "requestor": "Administrator", "material_request_no": mr.name, - "description": '_Test Item 1', + "item_code": '_Test Item', "quantity": 10.0, "unit_of_measurement": "_Test UOM", "status": "To Bill", @@ -58,9 +61,9 @@ class TestProcurementTracker(unittest.TestCase): "purchase_order": po.name, "supplier": "_Test Supplier", "estimated_cost": 0.0, - "actual_cost": None, - "purchase_order_amt": 5000.0, - "purchase_order_amt_in_company_currency": 300000.0, + "actual_cost": 0.0, + "purchase_order_amt": po.net_total, + "purchase_order_amt_in_company_currency": po.base_net_total, "expected_delivery_date": date_obj, "actual_delivery_date": date_obj } diff --git a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py index f1ffaf9d2d4..6013eaa29c6 100644 --- a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py +++ b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py @@ -6,4 +6,6 @@ import frappe def execute(): ''' Move from due_advance_amount to pending_amount ''' - frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') + + if frappe.db.has_column("Employee Advance", "due_advance_amount"): + frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') From 17ec1d6b28ba60e082b966abc4b9a055f5e67159 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 17 Jun 2020 13:46:21 +0530 Subject: [PATCH 654/929] fix: Message Formatting --- erpnext/controllers/stock_controller.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index b238600881d..ff6ac420208 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -225,7 +225,9 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Expense Account not set for Item {0}. Please set an Expense Account for the item in the Items table").format(item.item_code)) + frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense \ + Account in the Items table").format(item.idx, frappe.bold(item.item_code)), + title=_("Expense Account Missing")) else: is_expense_account = frappe.db.get_value("Account", From 7be4ba93518254e01bd7f4dbd71005b99cdfa638 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 17 Jun 2020 13:46:21 +0530 Subject: [PATCH 655/929] fix: Message Formatting (cherry picked from commit 17ec1d6b28ba60e082b966abc4b9a055f5e67159) --- erpnext/controllers/stock_controller.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index b238600881d..ff6ac420208 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -225,7 +225,9 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Expense Account not set for Item {0}. Please set an Expense Account for the item in the Items table").format(item.item_code)) + frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense \ + Account in the Items table").format(item.idx, frappe.bold(item.item_code)), + title=_("Expense Account Missing")) else: is_expense_account = frappe.db.get_value("Account", From e67195b0b9ff1cd260f97e44d3d92e3d07581a35 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 17 Jun 2020 19:06:56 +0530 Subject: [PATCH 656/929] fix: Typo --- erpnext/controllers/item_variant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 50b17abbe6d..1f95e004244 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -102,7 +102,7 @@ def validate_item_attribute_value(attributes_list, attribute, attribute_value, i frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format( frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value")) else: - msg = _("The value {0} is already assigned to an exisiting Item {1}.").format( + msg = _("The value {0} is already assigned to an existing Item {1}.").format( frappe.bold(attribute_value), frappe.bold(item)) msg += "
    " + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value")) From e256a7439e56d673c89ded9fe4e0c1e8eaa0369b Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 17 Jun 2020 19:06:56 +0530 Subject: [PATCH 657/929] fix: Typo --- erpnext/controllers/item_variant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 50b17abbe6d..1f95e004244 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -102,7 +102,7 @@ def validate_item_attribute_value(attributes_list, attribute, attribute_value, i frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format( frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value")) else: - msg = _("The value {0} is already assigned to an exisiting Item {1}.").format( + msg = _("The value {0} is already assigned to an existing Item {1}.").format( frappe.bold(attribute_value), frappe.bold(item)) msg += "
    " + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value")) From 1284b9b17d31836984ec708ad320d9ac766cd6ed Mon Sep 17 00:00:00 2001 From: P-Froggy <60393001+P-Froggy@users.noreply.github.com> Date: Thu, 18 Jun 2020 02:11:15 +0200 Subject: [PATCH 658/929] fix: Set Value of wrong Bank Account Field in Payment Entry Company bank account was wrongly inserted into the field "Party Bank Account" in payment entry, instead of "Bank Account". Adds filters for "Party Bank Account" based on selected Party (I forgot to create a PR for version-12 back in February, see commit #393a626). Also changes the label of "Default Bank Account" to "Default Company Bank Account", like suggested in PR #20632 #### Please Note This is kind of a revert of PR #19390. However, it seems like the function "get_party_bank_account" in Bank Account doctype changed how it worked since it is clearly pulling the default company bank account. --- .../accounts/doctype/payment_entry/payment_entry.js | 10 ++++++---- erpnext/buying/doctype/supplier/supplier.json | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 74be4c4ef0f..70c485133f0 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -25,7 +25,7 @@ frappe.ui.form.on('Payment Entry', { }); frm.set_query("party_type", function() { return{ - "filters": { + filters: { "name": ["in", Object.keys(frappe.boot.party_account_types)], } } @@ -33,14 +33,16 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("party_bank_account", function() { return { filters: { - "is_company_account":0 + is_company_account: 0, + party_type: frm.doc.party_type, + party: frm.doc.party } } }); frm.set_query("bank_account", function() { return { filters: { - "is_company_account":1 + is_company_account: 1 } } }); @@ -326,7 +328,7 @@ frappe.ui.form.on('Payment Entry', { () => { frm.set_party_account_based_on_party = false; if (r.message.bank_account) { - frm.set_value("party_bank_account", r.message.bank_account); + frm.set_value("bank_account", r.message.bank_account); } } ]); diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index e78abd62615..7a18160e39f 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -97,7 +97,7 @@ { "fieldname": "default_bank_account", "fieldtype": "Link", - "label": "Default Bank Account", + "label": "Default Company Bank Account", "options": "Bank Account" }, { @@ -385,7 +385,7 @@ "idx": 370, "image_field": "image", "links": [], - "modified": "2020-03-17 09:48:30.578242", + "modified": "2020-06-17 23:28:30", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", From 59938a46eb01093e114c0d4a065bfa350313f9b1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 09:46:03 +0530 Subject: [PATCH 659/929] fix: typo for language in Terms description (#22270) (#22272) (cherry picked from commit c159556c24dd90f18d2b5dafacd94abfacfff4d8) Co-authored-by: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com> --- .../doctype/terms_and_conditions/terms_and_conditions.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 aba6a791a4e..28d1d16a051 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:title", @@ -49,7 +50,7 @@ "fieldname": "terms_and_conditions_help", "fieldtype": "HTML", "label": "Terms and Conditions Help", - "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.

    " + "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 Language. To learn more about Jinja, read this documentation.

    " }, { "fieldname": "applicable_modules_section", @@ -81,7 +82,8 @@ ], "icon": "icon-legal", "idx": 1, - "modified": "2019-07-04 13:31:30.393425", + "links": [], + "modified": "2020-06-16 22:54:38.094844", "modified_by": "Administrator", "module": "Setup", "name": "Terms and Conditions", From 5d042ad4960266e372eab783a0d22a4e33aa8637 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 09:47:15 +0530 Subject: [PATCH 660/929] fix: update shopify api version (#22284) (#22299) Co-authored-by: Saurabh (cherry picked from commit 53b601523b81c32a0b5c10c721f99c184d09bcd8) Co-authored-by: Mangesh-Khairnar --- .../shopify_settings/shopify_settings.py | 24 +++++++++++++------ .../doctype/shopify_settings/sync_product.py | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py index 64c3b2d2730..25ffd281099 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py @@ -8,6 +8,7 @@ import json from frappe import _ from frappe.model.document import Document from frappe.utils import get_request_session +from requests.exceptions import HTTPError from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from erpnext.erpnext_integrations.utils import get_webhook_address from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log @@ -29,19 +30,24 @@ class ShopifySettings(Document): webhooks = ["orders/create", "orders/paid", "orders/fulfilled"] # url = get_shopify_url('admin/webhooks.json', self) created_webhooks = [d.method for d in self.webhooks] - url = get_shopify_url('admin/api/2019-04/webhooks.json', self) + url = get_shopify_url('admin/api/2020-04/webhooks.json', self) for method in webhooks: session = get_request_session() try: - d = session.post(url, data=json.dumps({ + res = session.post(url, data=json.dumps({ "webhook": { "topic": method, "address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'), "format": "json" } }), headers=get_header(self)) - d.raise_for_status() - self.update_webhook_table(method, d.json()) + res.raise_for_status() + self.update_webhook_table(method, res.json()) + + except HTTPError as e: + error_message = res.json().get('errors', e) + make_shopify_log(status="Warning", exception=error_message, rollback=True) + except Exception as e: make_shopify_log(status="Warning", exception=e, rollback=True) @@ -50,13 +56,18 @@ class ShopifySettings(Document): deleted_webhooks = [] for d in self.webhooks: - url = get_shopify_url('admin/api/2019-04/webhooks/{0}.json'.format(d.webhook_id), self) + url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self) try: res = session.delete(url, headers=get_header(self)) res.raise_for_status() deleted_webhooks.append(d) + + except HTTPError as e: + error_message = res.json().get('errors', e) + make_shopify_log(status="Warning", exception=error_message, rollback=True) + except Exception as e: - frappe.log_error(message=frappe.get_traceback(), title=e) + frappe.log_error(message=e, title='Shopify Webhooks Issue') for d in deleted_webhooks: self.remove(d) @@ -125,4 +136,3 @@ def setup_custom_fields(): } create_custom_fields(custom_fields) - diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py index bde101123db..f9f0bb3cecc 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py @@ -8,7 +8,7 @@ from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings impo shopify_variants_attr_list = ["option1", "option2", "option3"] def sync_item_from_shopify(shopify_settings, item): - url = get_shopify_url("admin/api/2019-04/products/{0}.json".format(item.get("product_id")), shopify_settings) + url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings) session = get_request_session() try: From 6a0bd556d7d68dc48cc59ac34cd7c08050fb21d0 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 18 Jun 2020 12:48:52 +0530 Subject: [PATCH 661/929] fix: update shopify api version (#22297) --- .../shopify_settings/shopify_settings.py | 24 +++++++++++++------ .../doctype/shopify_settings/sync_product.py | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py index 64c3b2d2730..25ffd281099 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py @@ -8,6 +8,7 @@ import json from frappe import _ from frappe.model.document import Document from frappe.utils import get_request_session +from requests.exceptions import HTTPError from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from erpnext.erpnext_integrations.utils import get_webhook_address from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log @@ -29,19 +30,24 @@ class ShopifySettings(Document): webhooks = ["orders/create", "orders/paid", "orders/fulfilled"] # url = get_shopify_url('admin/webhooks.json', self) created_webhooks = [d.method for d in self.webhooks] - url = get_shopify_url('admin/api/2019-04/webhooks.json', self) + url = get_shopify_url('admin/api/2020-04/webhooks.json', self) for method in webhooks: session = get_request_session() try: - d = session.post(url, data=json.dumps({ + res = session.post(url, data=json.dumps({ "webhook": { "topic": method, "address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'), "format": "json" } }), headers=get_header(self)) - d.raise_for_status() - self.update_webhook_table(method, d.json()) + res.raise_for_status() + self.update_webhook_table(method, res.json()) + + except HTTPError as e: + error_message = res.json().get('errors', e) + make_shopify_log(status="Warning", exception=error_message, rollback=True) + except Exception as e: make_shopify_log(status="Warning", exception=e, rollback=True) @@ -50,13 +56,18 @@ class ShopifySettings(Document): deleted_webhooks = [] for d in self.webhooks: - url = get_shopify_url('admin/api/2019-04/webhooks/{0}.json'.format(d.webhook_id), self) + url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self) try: res = session.delete(url, headers=get_header(self)) res.raise_for_status() deleted_webhooks.append(d) + + except HTTPError as e: + error_message = res.json().get('errors', e) + make_shopify_log(status="Warning", exception=error_message, rollback=True) + except Exception as e: - frappe.log_error(message=frappe.get_traceback(), title=e) + frappe.log_error(message=e, title='Shopify Webhooks Issue') for d in deleted_webhooks: self.remove(d) @@ -125,4 +136,3 @@ def setup_custom_fields(): } create_custom_fields(custom_fields) - diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py index bde101123db..f9f0bb3cecc 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py @@ -8,7 +8,7 @@ from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings impo shopify_variants_attr_list = ["option1", "option2", "option3"] def sync_item_from_shopify(shopify_settings, item): - url = get_shopify_url("admin/api/2019-04/products/{0}.json".format(item.get("product_id")), shopify_settings) + url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings) session = get_request_session() try: From c75236579f307c638264f54bfbec712dc3b1ba56 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 18 Jun 2020 13:59:19 +0530 Subject: [PATCH 662/929] fix: incorrect variable used while adding items in the submitted sales order --- erpnext/controllers/accounts_controller.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a4bed51f479..3e6c7dc788f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1194,11 +1194,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil action = "add" if perm_type == 'create' else "update" frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions")) - def get_new_child_item(): + def get_new_child_item(item_row): if parent_doctype == "Sales Order": - return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row) if parent_doctype == "Purchase Order": - return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row) def validate_quantity(child_item, d): if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): @@ -1219,7 +1219,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if not d.get("docname"): new_child_flag = True check_permissions(parent, 'create') - child_item = get_new_child_item() + child_item = get_new_child_item(d) else: check_permissions(parent, 'write') child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) From 82abcc94fae6ce969b5b5ca28bba953d2d1c6621 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 18 Jun 2020 13:59:19 +0530 Subject: [PATCH 663/929] fix: incorrect variable used while adding items in the submitted sales order --- erpnext/controllers/accounts_controller.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a4bed51f479..3e6c7dc788f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1194,11 +1194,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil action = "add" if perm_type == 'create' else "update" frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions")) - def get_new_child_item(): + def get_new_child_item(item_row): if parent_doctype == "Sales Order": - return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row) if parent_doctype == "Purchase Order": - return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row) def validate_quantity(child_item, d): if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): @@ -1219,7 +1219,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if not d.get("docname"): new_child_flag = True check_permissions(parent, 'create') - child_item = get_new_child_item() + child_item = get_new_child_item(d) else: check_permissions(parent, 'write') child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) From af6a0f3a9d091713fdc90574acb5275b0d89c061 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com> Date: Thu, 18 Jun 2020 14:34:11 +0530 Subject: [PATCH 664/929] fix(HR): typo in error message in Employee Balance Report (#22306) * fix: typo in date error message * fix: error message cleanup in Leave Balance report --- .../report/employee_leave_balance/employee_leave_balance.py | 4 ++-- .../employee_leave_balance_summary.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index d5050e73d46..0a6386f103f 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -49,7 +49,7 @@ def get_data(filters, leave_types): conditions = get_conditions(filters) if filters.to_date <= filters.from_date: - frappe.throw(_("From date can not be greater than than To date")) + frappe.throw(_("'From Date should be less than To Date")) active_employees = frappe.get_all("Employee", filters=conditions, @@ -160,4 +160,4 @@ def get_department_leave_approver_map(department=None): for k, v in approver_list: approvers.setdefault(k, []).append(v) - return approvers \ No newline at end of file + return approvers diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py index 21646425bb6..70510492be9 100644 --- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py +++ b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py @@ -10,7 +10,7 @@ from erpnext.hr.report.employee_leave_balance.employee_leave_balance import calc def execute(filters=None): if filters.to_date <= filters.from_date: - frappe.throw(_('From date can not be greater than than To date')) + frappe.throw(_('From Date should be less than To Date')) columns = get_columns() data = get_data(filters) From b865d58b8152c49fb6a190b7053814c15bb1900e Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 18 Jun 2020 14:43:31 +0530 Subject: [PATCH 665/929] fix: Patch simplification - Also, apply patch to all docs --- erpnext/patches/v12_0/set_multi_uom_in_rfq.py | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py index 50725bb69dd..70ca6b222e9 100644 --- a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py +++ b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py @@ -9,21 +9,8 @@ from erpnext.stock.get_item_details import get_conversion_factor def execute(): frappe.reload_doc('buying', 'doctype', 'request_for_quotation_item') - for rfq_item in frappe.db.sql("""SELECT name, item_code, uom, qty FROM `tabRequest for Quotation Item` WHERE docstatus<2""", as_dict=1): - item_code, uom, qty = rfq_item.get("item_code"), rfq_item.get("uom"), rfq_item.get("qty") - conversion_factor = get_conversion_factor(item_code, uom).get("conversion_factor") or 1.0 - - filters = { - "name" : rfq_item.get("name"), - "stock_uom" : frappe.db.get_value("Item", item_code, "stock_uom"), - "conversion_factor" : conversion_factor, - "stock_qty" : flt(qty) * flt(conversion_factor) - } - - frappe.db.sql("""UPDATE `tabRequest for Quotation Item` + frappe.db.sql("""UPDATE `tabRequest for Quotation Item` SET - stock_uom= %(stock_uom)s, - conversion_factor = %(conversion_factor)s, - stock_qty = %(stock_qty)s - WHERE - name = %(name)s""", filters) \ No newline at end of file + stock_uom = uom, + conversion_factor = 1, + stock_qty = qty""") \ No newline at end of file From 2417d9492f1994e98cb2f75378ac5e8ff20154f8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 15:28:39 +0530 Subject: [PATCH 666/929] fix(HR): typo in error message in Employee Balance Report (#22306) (#22313) * fix: typo in date error message * fix: error message cleanup in Leave Balance report (cherry picked from commit af6a0f3a9d091713fdc90574acb5275b0d89c061) Co-authored-by: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com> --- .../report/employee_leave_balance/employee_leave_balance.py | 4 ++-- .../employee_leave_balance_summary.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index d5050e73d46..0a6386f103f 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -49,7 +49,7 @@ def get_data(filters, leave_types): conditions = get_conditions(filters) if filters.to_date <= filters.from_date: - frappe.throw(_("From date can not be greater than than To date")) + frappe.throw(_("'From Date should be less than To Date")) active_employees = frappe.get_all("Employee", filters=conditions, @@ -160,4 +160,4 @@ def get_department_leave_approver_map(department=None): for k, v in approver_list: approvers.setdefault(k, []).append(v) - return approvers \ No newline at end of file + return approvers diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py index 21646425bb6..70510492be9 100644 --- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py +++ b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py @@ -10,7 +10,7 @@ from erpnext.hr.report.employee_leave_balance.employee_leave_balance import calc def execute(filters=None): if filters.to_date <= filters.from_date: - frappe.throw(_('From date can not be greater than than To date')) + frappe.throw(_('From Date should be less than To Date')) columns = get_columns() data = get_data(filters) From 0c08324fab230968b7e0b375363800c76374a6f2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 15:29:46 +0530 Subject: [PATCH 667/929] chore: add standard queries hooks to whitelist (#21939) (#22305) standard queries are used within the search widget, and now require to be whitelisted before they can be executed through the search widget. Signed-off-by: Chinmay D. Pai Co-authored-by: sahil28297 <37302950+sahil28297@users.noreply.github.com> (cherry picked from commit 96100e95077db9d9a2a27b384030460109cdd0f2) Co-authored-by: Chinmay Pai --- .../doctype/healthcare_practitioner/healthcare_practitioner.py | 1 + erpnext/selling/doctype/customer/customer.py | 1 + 2 files changed, 2 insertions(+) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py index ad32e946312..40f31016bc4 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py @@ -67,6 +67,7 @@ def validate_service_item(item, msg): if frappe.db.get_value("Item", item, "is_stock_item") == 1: frappe.throw(_(msg)) +@frappe.whitelist() def get_practitioner_list(doctype, txt, searchfield, start, page_len, filters=None): fields = ["name", "first_name", "mobile_phone"] diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 9a5d2ebd910..53c43fb2b60 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -299,6 +299,7 @@ def get_loyalty_programs(doc): return lp_details +@frappe.whitelist() def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None): from erpnext.controllers.queries import get_fields From 62882ea65b9b7a37b759dd202d718d70b547a5b4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 15:30:04 +0530 Subject: [PATCH 668/929] fix: asset maintenance fixes (#21277) (#22300) * fix: asset maintenance fixes * fix: tests (cherry picked from commit 9c494d3e72d95c6b95f379a0d00545a225f35c33) Co-authored-by: Saqib --- .../asset_maintenance/asset_maintenance.js | 39 ------------------- .../asset_maintenance/asset_maintenance.py | 7 ++-- .../test_asset_maintenance.py | 6 ++- 3 files changed, 7 insertions(+), 45 deletions(-) diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js index 3c135d466c1..001fc26ffe7 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js @@ -24,26 +24,6 @@ frappe.ui.form.on('Asset Maintenance', { return indicator; } ); - - frm.set_query('select_serial_no', function(doc){ - return { - asset: frm.doc.asset_name - } - }) - }, - - select_serial_no: (frm) => { - let serial_nos = frm.doc.serial_no || frm.doc.select_serial_no; - if (serial_nos) { - serial_nos = serial_nos.split('\n'); - serial_nos.push(frm.doc.select_serial_no); - - const unique_sn = serial_nos.filter(function(elem, index, self) { - return index === self.indexOf(elem); - }); - - frm.set_value("serial_no", unique_sn.join('\n')); - } }, refresh: (frm) => { @@ -93,25 +73,6 @@ frappe.ui.form.on('Asset Maintenance Task', { }, end_date: (frm, cdt, cdn) => { get_next_due_date(frm, cdt, cdn); - }, - assign_to: (frm, cdt, cdn) => { - var d = locals[cdt][cdn]; - if (frm.doc.__islocal) { - frappe.model.set_value(cdt, cdn, "assign_to", ""); - frappe.model.set_value(cdt, cdn, "assign_to_name", ""); - frappe.throw(__("Please save before assigning task.")); - } - if (d.assign_to) { - return frappe.call({ - method: 'erpnext.assets.doctype.asset_maintenance.asset_maintenance.assign_tasks', - args: { - asset_maintenance_name: frm.doc.name, - assign_to_member: d.assign_to, - maintenance_task: d.maintenance_task, - next_due_date: d.next_due_date - } - }); - } } }); diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index ba63dd661d7..d6adde6a371 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -16,12 +16,11 @@ class AssetMaintenance(Document): throw(_("Start date should be less than end date for task {0}").format(task.maintenance_task)) if getdate(task.next_due_date) < getdate(nowdate()): task.maintenance_status = "Overdue" + if not task.assign_to and self.docstatus == 0: + throw(_("Row #{}: Please asign task to a member.").format(task.idx)) def on_update(self): for task in self.get('asset_maintenance_tasks'): - if not task.assign_to: - task.db_set("assign_to", self.maintenance_manager) - task.db_set("assign_to_name", self.maintenance_manager_name) assign_tasks(self.name, task.assign_to, task.maintenance_task, task.next_due_date) self.sync_maintenance_tasks() @@ -108,7 +107,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task): @frappe.whitelist() def get_team_members(doctype, txt, searchfield, start, page_len, filters): - return frappe.db.get_values('Maintenance Team Member', {'parent':filters.get("maintenance_team")}) + return frappe.db.get_values('Maintenance Team Member', { 'parent': filters.get("maintenance_team") }) @frappe.whitelist() def get_maintenance_log(asset_name): diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py index 6c2fd67a9af..392fbdd2af7 100644 --- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py @@ -125,13 +125,15 @@ def get_maintenance_tasks(): "start_date": nowdate(), "periodicity": "Monthly", "maintenance_type": "Preventive Maintenance", - "maintenance_status": "Planned" + "maintenance_status": "Planned", + "assign_to": "marcus@abc.com" }, {"maintenance_task": "Check Gears", "start_date": nowdate(), "periodicity": "Yearly", "maintenance_type": "Calibration", - "maintenance_status": "Planned" + "maintenance_status": "Planned", + "assign_to": "thalia@abc.com" } ] From 1ca4370033fd8e8cfb65c236ee6270aa63f15750 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 15:39:13 +0530 Subject: [PATCH 669/929] fix: asset maintenance fixes (#21277) (#22294) * fix: asset maintenance fixes * fix: tests (cherry picked from commit 9c494d3e72d95c6b95f379a0d00545a225f35c33) Co-authored-by: Saqib --- .../asset_maintenance/asset_maintenance.js | 39 ------------------- .../asset_maintenance/asset_maintenance.py | 7 ++-- .../test_asset_maintenance.py | 6 ++- 3 files changed, 7 insertions(+), 45 deletions(-) diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js index 3c135d466c1..001fc26ffe7 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js @@ -24,26 +24,6 @@ frappe.ui.form.on('Asset Maintenance', { return indicator; } ); - - frm.set_query('select_serial_no', function(doc){ - return { - asset: frm.doc.asset_name - } - }) - }, - - select_serial_no: (frm) => { - let serial_nos = frm.doc.serial_no || frm.doc.select_serial_no; - if (serial_nos) { - serial_nos = serial_nos.split('\n'); - serial_nos.push(frm.doc.select_serial_no); - - const unique_sn = serial_nos.filter(function(elem, index, self) { - return index === self.indexOf(elem); - }); - - frm.set_value("serial_no", unique_sn.join('\n')); - } }, refresh: (frm) => { @@ -93,25 +73,6 @@ frappe.ui.form.on('Asset Maintenance Task', { }, end_date: (frm, cdt, cdn) => { get_next_due_date(frm, cdt, cdn); - }, - assign_to: (frm, cdt, cdn) => { - var d = locals[cdt][cdn]; - if (frm.doc.__islocal) { - frappe.model.set_value(cdt, cdn, "assign_to", ""); - frappe.model.set_value(cdt, cdn, "assign_to_name", ""); - frappe.throw(__("Please save before assigning task.")); - } - if (d.assign_to) { - return frappe.call({ - method: 'erpnext.assets.doctype.asset_maintenance.asset_maintenance.assign_tasks', - args: { - asset_maintenance_name: frm.doc.name, - assign_to_member: d.assign_to, - maintenance_task: d.maintenance_task, - next_due_date: d.next_due_date - } - }); - } } }); diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index ba63dd661d7..d6adde6a371 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -16,12 +16,11 @@ class AssetMaintenance(Document): throw(_("Start date should be less than end date for task {0}").format(task.maintenance_task)) if getdate(task.next_due_date) < getdate(nowdate()): task.maintenance_status = "Overdue" + if not task.assign_to and self.docstatus == 0: + throw(_("Row #{}: Please asign task to a member.").format(task.idx)) def on_update(self): for task in self.get('asset_maintenance_tasks'): - if not task.assign_to: - task.db_set("assign_to", self.maintenance_manager) - task.db_set("assign_to_name", self.maintenance_manager_name) assign_tasks(self.name, task.assign_to, task.maintenance_task, task.next_due_date) self.sync_maintenance_tasks() @@ -108,7 +107,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task): @frappe.whitelist() def get_team_members(doctype, txt, searchfield, start, page_len, filters): - return frappe.db.get_values('Maintenance Team Member', {'parent':filters.get("maintenance_team")}) + return frappe.db.get_values('Maintenance Team Member', { 'parent': filters.get("maintenance_team") }) @frappe.whitelist() def get_maintenance_log(asset_name): diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py index 6c2fd67a9af..392fbdd2af7 100644 --- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py @@ -125,13 +125,15 @@ def get_maintenance_tasks(): "start_date": nowdate(), "periodicity": "Monthly", "maintenance_type": "Preventive Maintenance", - "maintenance_status": "Planned" + "maintenance_status": "Planned", + "assign_to": "marcus@abc.com" }, {"maintenance_task": "Check Gears", "start_date": nowdate(), "periodicity": "Yearly", "maintenance_type": "Calibration", - "maintenance_status": "Planned" + "maintenance_status": "Planned", + "assign_to": "thalia@abc.com" } ] From f9dbcef5c9170e076610f1c082ccfa1e711ab9c8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 18 Jun 2020 16:45:16 +0530 Subject: [PATCH 670/929] fix: Do not copy Item Tax template from SO to PO --- erpnext/selling/doctype/sales_order/sales_order.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 05e4aa892b0..ffb66354fa0 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -868,7 +868,8 @@ def make_purchase_order(source_name, for_supplier=None, selected_items=[], targe ], "field_no_map": [ "rate", - "price_list_rate" + "price_list_rate", + "item_tax_template" ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier and doc.item_code in selected_items From 531ef15e30b91eaab139b0e587afb25e2ec3da15 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 17:04:36 +0530 Subject: [PATCH 671/929] refactor: handle exceptions when updating addresses (#22307) (#22315) * refactor: handle exceptions when updating addresses * refactor: fold common statements in a loop (cherry picked from commit 34d2bfbb3e6ce28de2690812191b8885b4f8f6ab) Co-authored-by: Shivam Mishra --- .../connectors/woocommerce_connection.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 1b0c9f60b6e..6dedaa8c530 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -73,10 +73,16 @@ def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name if customer_exists: frappe.rename_doc("Customer", old_name, customer_name) - billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"}) - shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"}) - rename_address(billing_address, customer) - rename_address(shipping_address, customer) + for address_type in ("Billing", "Shipping",): + try: + address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type}) + rename_address(address, customer) + except ( + frappe.DoesNotExistError, + frappe.DuplicateEntryError, + frappe.ValidationError, + ): + pass else: create_address(raw_billing_data, customer, "Billing") create_address(raw_shipping_data, customer, "Shipping") From 5bd479e8ee5e9b2aba6ff6b9d61bc09f54bafece Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 17:04:45 +0530 Subject: [PATCH 672/929] refactor: handle exceptions when updating addresses (#22307) (#22316) * refactor: handle exceptions when updating addresses * refactor: fold common statements in a loop (cherry picked from commit 34d2bfbb3e6ce28de2690812191b8885b4f8f6ab) Co-authored-by: Shivam Mishra --- .../connectors/woocommerce_connection.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 1b0c9f60b6e..6dedaa8c530 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -73,10 +73,16 @@ def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name if customer_exists: frappe.rename_doc("Customer", old_name, customer_name) - billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"}) - shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"}) - rename_address(billing_address, customer) - rename_address(shipping_address, customer) + for address_type in ("Billing", "Shipping",): + try: + address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type}) + rename_address(address, customer) + except ( + frappe.DoesNotExistError, + frappe.DuplicateEntryError, + frappe.ValidationError, + ): + pass else: create_address(raw_billing_data, customer, "Billing") create_address(raw_shipping_data, customer, "Shipping") From f53ee9026b98ca4ef777b76781a5e7b42a159445 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 18 Jun 2020 18:13:51 +0530 Subject: [PATCH 673/929] chore: Added Change log --- erpnext/change_log/v12/v12_10_0.md | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 erpnext/change_log/v12/v12_10_0.md diff --git a/erpnext/change_log/v12/v12_10_0.md b/erpnext/change_log/v12/v12_10_0.md new file mode 100644 index 00000000000..702af1fd27f --- /dev/null +++ b/erpnext/change_log/v12/v12_10_0.md @@ -0,0 +1,54 @@ +## ERPNext v12.9.0 Release Note + +### Enhancements + +- Create Shipping Address and Contact in ERPNext from the woocommerece platform ([#21654](https://github.com/frappe/erpnext/pull/21654)) +- Dashboard in Course and Assessment Plan ([#21889](https://github.com/frappe/erpnext/pull/21889)) +- Added conversion factor for pound, gram to Ounce ([#21710](https://github.com/frappe/erpnext/pull/21710)) +- Allowed renaming for Sales Stage ([#21800](https://github.com/frappe/erpnext/pull/21800)) +- Add filter for cost center in expense table ([#22209](https://github.com/frappe/erpnext/pull/22209)) +- Project filter in Trial Balance Report ([#21815](https://github.com/frappe/erpnext/pull/21815)) +- Added column Expired Leave in Leave Application dashboard ([#21859](https://github.com/frappe/erpnext/pull/21859)) +- Auto set serial nos and batches only if allowed in Stock Settings ([#21779](https://github.com/frappe/erpnext/pull/21779)) +- Filter batches based on selected item and warehouse in Pick List ([#21778](https://github.com/frappe/erpnext/pull/21778)) +- Errored documents handling while migrating data from Tally ([#22079](https://github.com/frappe/erpnext/pull/22079)) + +### Fixes + +- Routing operations table is blank on pull of operations in BOM ([#22040](https://github.com/frappe/erpnext/pull/22040)) +- Cannot make payment entry against shareholder ([#21597](https://github.com/frappe/erpnext/pull/21597)) +- Do not add filters in report on accounting dimension creation if it already exists ([#21941](https://github.com/frappe/erpnext/pull/21941)) +- Post Dated unallocated amount not considered in Advance Amount in AR/AP summary ([#21838](https://github.com/frappe/erpnext/pull/21838)) +- Check for Company before rendering tree in Account Tree ([#22204](https://github.com/frappe/erpnext/pull/22204)) +- Loyalty point entry use wrong tier ([#22168](https://github.com/frappe/erpnext/pull/22168)) +- Added Inactive serial no status ([#21849](https://github.com/frappe/erpnext/pull/21849)) +- Routing operations not added sequentially in the BOM ([#22110](https://github.com/frappe/erpnext/pull/22110)) +- TDS computation summary report ([#21987](https://github.com/frappe/erpnext/pull/21987)) +- Validate Payment Gateway only if it exists in Payment Request. ([#21806](https://github.com/frappe/erpnext/pull/21806)) +- Finished Product Valuation at Repack ([#22148](https://github.com/frappe/erpnext/pull/22148)) +- Wrong Ordered-Status Indicator for Material Request Items ([#22117](https://github.com/frappe/erpnext/pull/22117)) +- In-state Invoice not appearing in GSTR-1 report (India) ([#21787](https://github.com/frappe/erpnext/pull/21787)) +- Tax amount in GSTR-1 JSON (India) ([#21791](https://github.com/frappe/erpnext/pull/21791)) +- Fetch depreciation amount only if depreciation entry is made ([#21894](https://github.com/frappe/erpnext/pull/21894)) +- Throw error if no serial numbers are found in Pick List ([#21914](https://github.com/frappe/erpnext/pull/21914)) +- Shopify error message on failure of sales order creation ([#21924](https://github.com/frappe/erpnext/pull/21924)) +- Don't prompt for Quality Inspection on Return Documents. ([#22200](https://github.com/frappe/erpnext/pull/22200)) +- Item tax template not getting mapped from source to target doc ([#21863](https://github.com/frappe/erpnext/pull/21863)) +- Create purchase invoice from purchase receipt dashboard ([#22087](https://github.com/frappe/erpnext/pull/22087)) +- Procurement Tracker Data Consistency ([#22062](https://github.com/frappe/erpnext/pull/22062)) +- Cannot assign same task to other asset maintenance ([#22024](https://github.com/frappe/erpnext/pull/22024)) +- Incorrect VAT rate display in Sales Invoice (UAE) ([#21883](https://github.com/frappe/erpnext/pull/21883)) +- Missing income account when getting free product ([#22158](https://github.com/frappe/erpnext/pull/22158)) +- Item tax template not applied if valid from is blank ([#21819](https://github.com/frappe/erpnext/pull/21819)) +- Tax ID is not fetched when creating Sales Order from Quotation ([#21786](https://github.com/frappe/erpnext/pull/21786)) +- Disposed asset creates inconsistencies in asset depreciation report ([#22021](https://github.com/frappe/erpnext/pull/22021)) +- Submitted sales order can be updated with proper permission ([#22218](https://github.com/frappe/erpnext/pull/22218)) +- Import supplier invoice not working ([#22108](https://github.com/frappe/erpnext/pull/22108)) +- Showing Wrong balance on allocation boundary dates ([#21908](https://github.com/frappe/erpnext/pull/21908)) +- Fetch customer into Delivery Note from Pick List ([#21825](https://github.com/frappe/erpnext/pull/21825)) +- Apply shipping rule without address ([#22093](https://github.com/frappe/erpnext/pull/22093)) +- Prioritize Default Customer Price List in Portal ([#22183](https://github.com/frappe/erpnext/pull/22183)) +- Supplier Invoice No not fetched in Import Supplier Invoice ([#21829](https://github.com/frappe/erpnext/pull/21829)) +- Item Price and Add to Cart not showing on Website ([#21905](https://github.com/frappe/erpnext/pull/21905)) +- Make transaction date of the oldest transaction as the last integration date ([#22017](https://github.com/frappe/erpnext/pull/22017)) +- Misleading Error message for Item Attribute ([#22068](https://github.com/frappe/erpnext/pull/22068)) \ No newline at end of file From fc488a2b464f1ae84683578536a46d094fcf0161 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 18 Jun 2020 18:19:49 +0530 Subject: [PATCH 674/929] fix: Do not copy Item Tax template from SO to PO (#22326) --- erpnext/selling/doctype/sales_order/sales_order.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 05e4aa892b0..ffb66354fa0 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -868,7 +868,8 @@ def make_purchase_order(source_name, for_supplier=None, selected_items=[], targe ], "field_no_map": [ "rate", - "price_list_rate" + "price_list_rate", + "item_tax_template" ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier and doc.item_code in selected_items From cce1116b4e2adad6e061f1965e13bc960a756aa2 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 18 Jun 2020 19:15:49 +0530 Subject: [PATCH 675/929] fix: Quality procedure fixes (#22287) * fix: Quality Procedure Fixes - Dont prompt error message if parent is the same in child - filter child procedure field - Disable New button in tree view - Editable grid for Quality Procedure Proces table. * chore: Remove unnecessary get_doc * fix: Codacy Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> --- .../quality_procedure/quality_procedure.js | 9 +++++++ .../quality_procedure/quality_procedure.json | 3 ++- .../quality_procedure/quality_procedure.py | 25 +++++++++++-------- .../quality_procedure_tree.js | 1 + .../quality_procedure_process.json | 5 +++- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js index ded3a51dd60..cf2644e0053 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js @@ -2,4 +2,13 @@ // For license information, please see license.txt frappe.ui.form.on('Quality Procedure', { + refresh: function(frm) { + frm.set_query("procedure","processes", (frm) =>{ + return { + filters: { + name: ["not in", [frm.parent_quality_procedure, frm.name]] + } + }; + }); + } }); \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json index e44e0733967..b3c0d948909 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_rename": 1, "autoname": "format:PRC-{quality_procedure_name}", "creation": "2018-10-06 00:06:29.756804", "doctype": "DocType", @@ -72,7 +73,7 @@ ], "is_tree": 1, "links": [], - "modified": "2020-03-18 18:26:05.511984", + "modified": "2020-06-17 17:25:03.434953", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure", diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index 3ce5ea015f5..1952e578673 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -10,13 +10,8 @@ from frappe import _ class QualityProcedure(NestedSet): nsm_parent_field = 'parent_quality_procedure' - def on_save(self): - for process in self.processes: - if process.procedure: - doc = frappe.get_doc("Quality Procedure", process.procedure) - if doc.parent_quality_procedure: - frappe.throw(_("{0} already has a Parent Procedure {1}.".format(process.procedure, doc.parent_quality_procedure))) - self.is_group = 1 + def before_save(self): + self.check_for_incorrect_child() def on_update(self): self.set_parent() @@ -47,11 +42,21 @@ class QualityProcedure(NestedSet): doc.save(ignore_permissions=True) def set_parent(self): + for process in self.processes: + # Set parent for only those children who don't have a parent + parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") + if not parent_quality_procedure and process.procedure: + frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name) + + def check_for_incorrect_child(self): for process in self.processes: if process.procedure: - doc = frappe.get_doc("Quality Procedure", process.procedure) - doc.parent_quality_procedure = self.name - doc.save(ignore_permissions=True) + # Check if any child process belongs to another parent. + parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") + if parent_quality_procedure and parent_quality_procedure != self.name: + frappe.throw(_("{0} already has a Parent Procedure {1}.".format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure))), + title=_("Invalid Child Procedure")) + self.is_group = 1 @frappe.whitelist() def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False): diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js index 6df6f656aa1..ef48ab6c6e2 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js @@ -16,6 +16,7 @@ frappe.treeview_settings["Quality Procedure"] = { }, ], breadcrumb: "Setup", + disable_add_node: true, root_label: "All Quality Procedures", get_tree_root: false, menu_items: [ diff --git a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json index 0a67fa505ee..3925dbb8aca 100644 --- a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json +++ b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json @@ -1,6 +1,8 @@ { + "actions": [], "creation": "2019-05-26 00:10:00.248885", "doctype": "DocType", + "editable_grid": 1, "engine": "InnoDB", "field_order": [ "process_description", @@ -23,7 +25,8 @@ } ], "istable": 1, - "modified": "2019-05-26 22:05:49.007189", + "links": [], + "modified": "2020-06-17 15:44:38.937915", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure Process", From a9ed42f93d106f016a0c014b7e7539598986e6a5 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 18 Jun 2020 19:15:49 +0530 Subject: [PATCH 676/929] fix: Quality procedure fixes (#22287) * fix: Quality Procedure Fixes - Dont prompt error message if parent is the same in child - filter child procedure field - Disable New button in tree view - Editable grid for Quality Procedure Proces table. * chore: Remove unnecessary get_doc * fix: Codacy Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> (cherry picked from commit cce1116b4e2adad6e061f1965e13bc960a756aa2) --- .../quality_procedure/quality_procedure.js | 9 +++++++ .../quality_procedure/quality_procedure.json | 3 ++- .../quality_procedure/quality_procedure.py | 25 +++++++++++-------- .../quality_procedure_tree.js | 1 + .../quality_procedure_process.json | 5 +++- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js index ded3a51dd60..cf2644e0053 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js @@ -2,4 +2,13 @@ // For license information, please see license.txt frappe.ui.form.on('Quality Procedure', { + refresh: function(frm) { + frm.set_query("procedure","processes", (frm) =>{ + return { + filters: { + name: ["not in", [frm.parent_quality_procedure, frm.name]] + } + }; + }); + } }); \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json index e44e0733967..b3c0d948909 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_rename": 1, "autoname": "format:PRC-{quality_procedure_name}", "creation": "2018-10-06 00:06:29.756804", "doctype": "DocType", @@ -72,7 +73,7 @@ ], "is_tree": 1, "links": [], - "modified": "2020-03-18 18:26:05.511984", + "modified": "2020-06-17 17:25:03.434953", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure", diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index 3ce5ea015f5..1952e578673 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -10,13 +10,8 @@ from frappe import _ class QualityProcedure(NestedSet): nsm_parent_field = 'parent_quality_procedure' - def on_save(self): - for process in self.processes: - if process.procedure: - doc = frappe.get_doc("Quality Procedure", process.procedure) - if doc.parent_quality_procedure: - frappe.throw(_("{0} already has a Parent Procedure {1}.".format(process.procedure, doc.parent_quality_procedure))) - self.is_group = 1 + def before_save(self): + self.check_for_incorrect_child() def on_update(self): self.set_parent() @@ -47,11 +42,21 @@ class QualityProcedure(NestedSet): doc.save(ignore_permissions=True) def set_parent(self): + for process in self.processes: + # Set parent for only those children who don't have a parent + parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") + if not parent_quality_procedure and process.procedure: + frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name) + + def check_for_incorrect_child(self): for process in self.processes: if process.procedure: - doc = frappe.get_doc("Quality Procedure", process.procedure) - doc.parent_quality_procedure = self.name - doc.save(ignore_permissions=True) + # Check if any child process belongs to another parent. + parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") + if parent_quality_procedure and parent_quality_procedure != self.name: + frappe.throw(_("{0} already has a Parent Procedure {1}.".format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure))), + title=_("Invalid Child Procedure")) + self.is_group = 1 @frappe.whitelist() def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False): diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js index 6df6f656aa1..ef48ab6c6e2 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js @@ -16,6 +16,7 @@ frappe.treeview_settings["Quality Procedure"] = { }, ], breadcrumb: "Setup", + disable_add_node: true, root_label: "All Quality Procedures", get_tree_root: false, menu_items: [ diff --git a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json index 0a67fa505ee..3925dbb8aca 100644 --- a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json +++ b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json @@ -1,6 +1,8 @@ { + "actions": [], "creation": "2019-05-26 00:10:00.248885", "doctype": "DocType", + "editable_grid": 1, "engine": "InnoDB", "field_order": [ "process_description", @@ -23,7 +25,8 @@ } ], "istable": 1, - "modified": "2019-05-26 22:05:49.007189", + "links": [], + "modified": "2020-06-17 15:44:38.937915", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure Process", From a9dbff0eaeb48e60358dcc6552bf91450fe68c15 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 18 Jun 2020 20:03:00 +0530 Subject: [PATCH 677/929] chore: correct version in release note --- erpnext/change_log/v12/v12_10_0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/change_log/v12/v12_10_0.md b/erpnext/change_log/v12/v12_10_0.md index 702af1fd27f..07aa885ec82 100644 --- a/erpnext/change_log/v12/v12_10_0.md +++ b/erpnext/change_log/v12/v12_10_0.md @@ -1,4 +1,4 @@ -## ERPNext v12.9.0 Release Note +## ERPNext v12.10.0 Release Note ### Enhancements @@ -51,4 +51,4 @@ - Supplier Invoice No not fetched in Import Supplier Invoice ([#21829](https://github.com/frappe/erpnext/pull/21829)) - Item Price and Add to Cart not showing on Website ([#21905](https://github.com/frappe/erpnext/pull/21905)) - Make transaction date of the oldest transaction as the last integration date ([#22017](https://github.com/frappe/erpnext/pull/22017)) -- Misleading Error message for Item Attribute ([#22068](https://github.com/frappe/erpnext/pull/22068)) \ No newline at end of file +- Misleading Error message for Item Attribute ([#22068](https://github.com/frappe/erpnext/pull/22068)) From aa27a0e85c0f5a79c20cac1a64967dc90da18ea9 Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Thu, 18 Jun 2020 20:26:18 +0550 Subject: [PATCH 678/929] bumped to version 12.10.0 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 701217759c2..a46d4ad58ff 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.9.4' +__version__ = '12.10.0' def get_default_company(user=None): '''Get default company for user''' From acf399c4e0c74458c17866be7d72788aeecbe829 Mon Sep 17 00:00:00 2001 From: Poranut Chollavorn Date: Thu, 18 Jun 2020 14:41:26 +0000 Subject: [PATCH 679/929] fix(pricing_rule): apply_on logic dont get cleanup --- .../accounts/doctype/pricing_rule/pricing_rule.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 983f1ef85a0..a48aab119cc 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -84,9 +84,15 @@ class PricingRule(Document): for f in options: if not f: continue - f = frappe.scrub(f) - if f!=fieldname: - self.set(f, None) + scrubbed_f = frappe.scrub(f) + + if logic_field == 'apply_on': + apply_on_f = apply_on_dict.get(f, f) + else: + apply_on_f = scrubbed_f + + if scrubbed_f != fieldname: + self.set(apply_on_f, None) if self.mixed_conditions and self.get("same_item"): self.same_item = 0 From 67361bef73d2b0cf90a6086e863fa2b2fb11733c Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 18 Jun 2020 21:47:36 +0530 Subject: [PATCH 680/929] fix: Unabled to create batched item --- erpnext/stock/doctype/item/item.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index f5ffe242a1f..0cc8b03ab49 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -883,7 +883,12 @@ class Item(WebsiteGenerator): linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item"] for doctype in linked_doctypes: - if frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \ + if doctype in ("Purchase Invoice Item", "Sales Invoice Item",): + # If Invoice has Stock impact, only then consider it. + if self.stock_ledger_created(): + return True + + elif frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \ frappe.db.get_value("Production Order", filters={"production_item": self.name, "docstatus": 1}): return True From 10624f3fa65071ce3af02ee2b5f2c9141ac2b6ea Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Thu, 18 Jun 2020 23:23:54 +0530 Subject: [PATCH 681/929] fix: Customer Group label in Itemwise Sales report (cherry picked from commit 766f978858b2929ae2caa6a80596b76b850e13fe) --- .../report/item_wise_sales_history/item_wise_sales_history.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index 405004ece54..08a98ba6c07 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -96,7 +96,7 @@ def get_columns(filters): "label": _("Customer Group"), "fieldtype": "Link", "fieldname": "customer_group", - "options": "customer Group", + "options": "Customer Group", "width": 120 }, { From 4793e9c24c1c7170afdcf83e253a4a2a312f9bd6 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Thu, 18 Jun 2020 23:23:54 +0530 Subject: [PATCH 682/929] fix: Customer Group label in Itemwise Sales report (cherry picked from commit 766f978858b2929ae2caa6a80596b76b850e13fe) --- .../report/item_wise_sales_history/item_wise_sales_history.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index 405004ece54..08a98ba6c07 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -96,7 +96,7 @@ def get_columns(filters): "label": _("Customer Group"), "fieldtype": "Link", "fieldname": "customer_group", - "options": "customer Group", + "options": "Customer Group", "width": 120 }, { From 6f3e5e1891e265aa61c4748d3c4800476220b92d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2020 11:10:42 +0530 Subject: [PATCH 683/929] refactor: show service instead of services (#22339) (cherry picked from commit 25702a1c550069845d8ce1fea5aade1770ca7c64) Co-authored-by: Shivam Mishra --- .../templates/includes/footer/footer_powered.html | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html index cf7661ee3fa..4274ba12cf5 100644 --- a/erpnext/templates/includes/footer/footer_powered.html +++ b/erpnext/templates/includes/footer/footer_powered.html @@ -10,9 +10,17 @@ 'Agriculture': '/agriculture', 'Hospitality': '' } %} + {% set link = '' %} +{% set label = domains[0].domain %} {% if domains %} - {% set link = links[domains[0].domain] %} + {% set link = links[label] %} {% endif %} -Powered by ERPNext - {{ '' if domains else 'Open Source' }} ERP Software {{ ('for ' + domains[0].domain + ' Companies') if domains else '' }} +{% if label == "Services" %} + {% set label = "Service" %} +{% endif %} + + + +Powered by ERPNext - {{ '' if domains else 'Open Source' }} ERP Software {{ ('for ' + label + ' Companies') if domains else '' }} From 168d070d73c11065f7f5dc0ee1ab40d5f5f919c6 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Thu, 18 Jun 2020 14:20:22 +0530 Subject: [PATCH 684/929] email digest html fix (cherry picked from commit bd449c8d0794e359bf64d5aff8e72e6e91ff3c59) --- erpnext/setup/doctype/email_digest/email_digest.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index 0bcddc21517..662cda26bfb 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -101,8 +101,7 @@ class EmailDigest(Document): if not context.purchase_order_list: frappe.throw(_("No items to be received are overdue")) - if not (context.events or context.todo_list or context.notifications or context.cards - or context.purchase_orders_items_overdue_list): + if not context: return None frappe.flags.ignore_account_permission = False From 5acc06dfe6773ae5a77bab7c656cf4ec282eb6c4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2020 11:58:51 +0530 Subject: [PATCH 685/929] fix: Student Admission (#22280) * Student Admission fix * adding check for application * adding check for application * updating error message * added date_diff for date comparision (cherry picked from commit c6592c880a2873adcb7bf78883e9c3f20b25e59b) Co-authored-by: Anupam Kumar Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> --- .../student_admission/student_admission.json | 477 ++++-------------- .../templates/student_admission.html | 13 +- .../test_student_admission.js | 4 +- .../student_admission_program.json | 294 +++-------- .../student_applicant/student_applicant.py | 21 +- .../student_applicant/student_applicant.json | 406 ++++++++------- .../generators/student_admission.html | 6 +- 7 files changed, 416 insertions(+), 805 deletions(-) diff --git a/erpnext/education/doctype/student_admission/student_admission.json b/erpnext/education/doctype/student_admission/student_admission.json index b3c10d43316..1096888d4d2 100644 --- a/erpnext/education/doctype/student_admission/student_admission.json +++ b/erpnext/education/doctype/student_admission/student_admission.json @@ -1,398 +1,119 @@ { - "allow_copy": 0, - "allow_guest_to_view": 1, - "allow_import": 0, - "allow_rename": 1, - "autoname": "", - "beta": 0, - "creation": "2016-09-13 03:05:27.154713", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, + "actions": [], + "allow_guest_to_view": 1, + "allow_rename": 1, + "creation": "2016-09-13 03:05:27.154713", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "route", + "column_break_3", + "academic_year", + "admission_start_date", + "admission_end_date", + "published", + "enable_admission_application", + "section_break_5", + "program_details", + "introduction" + ], "fields": [ { - "allow_bulk_edit": 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": 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": "title", + "fieldtype": "Data", + "label": "Title" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "route", - "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": "Route", - "length": 0, - "no_copy": 1, - "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, + "fieldname": "route", + "fieldtype": "Data", + "label": "Route", + "no_copy": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "application_form_route", - "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": "Application Form Route", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "academic_year", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Academic Year", + "no_copy": 1, + "options": "Academic Year", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "academic_year", - "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": "Academic Year", - "length": 0, - "no_copy": 1, - "options": "Academic Year", - "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 - }, + "fieldname": "admission_start_date", + "fieldtype": "Date", + "label": "Admission Start Date", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_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": "Admission Start Date", - "length": 0, - "no_copy": 1, - "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": "admission_end_date", + "fieldtype": "Date", + "label": "Admission End Date", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_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": "Admission End Date", - "length": 0, - "no_copy": 1, - "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 - }, + "default": "0", + "fieldname": "published", + "fieldtype": "Check", + "label": "Publish on website" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "published", - "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": "Publish on 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": "section_break_5", + "fieldtype": "Section Break", + "label": "Eligibility and Details" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Eligibility and Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "program_details", + "fieldtype": "Table", + "label": "Eligibility and Details", + "options": "Student Admission Program" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program_details", - "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": "Eligibility and Details", - "length": 0, - "no_copy": 0, - "options": "Student Admission Program", - "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": "introduction", + "fieldtype": "Text Editor", + "label": "Introduction" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "introduction", - "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": "Introduction", - "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 + "default": "0", + "fieldname": "enable_admission_application", + "fieldtype": "Check", + "label": "Enable Admission Application" } - ], - "has_web_view": 1, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_published_field": "published", - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-11-10 18:57:34.570376", - "modified_by": "Administrator", - "module": "Education", - "name": "Student Admission", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 1, + "is_published_field": "published", + "links": [], + "modified": "2020-06-15 20:18:38.591626", + "modified_by": "Administrator", + "module": "Education", + "name": "Student Admission", + "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": "Academics User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "route": "admissions", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "title", - "track_changes": 0, - "track_seen": 0 + ], + "restrict_to_domain": "Education", + "route": "admissions", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "title" } \ No newline at end of file diff --git a/erpnext/education/doctype/student_admission/templates/student_admission.html b/erpnext/education/doctype/student_admission/templates/student_admission.html index 25afaca84dc..e5a9ead31ed 100644 --- a/erpnext/education/doctype/student_admission/templates/student_admission.html +++ b/erpnext/education/doctype/student_admission/templates/student_admission.html @@ -43,8 +43,8 @@ Program/Std. - Minumum Age(DOB) - Maximum Age(DOB) + Minumum Age + Maximum Age Application Fee @@ -52,8 +52,8 @@ {% for row in program_details %} {{ row.program }} - {{ row.minimum_age }} - {{ row.maximum_age }} + {{ row.min_age }} + {{ row.max_age }} {{ row.application_fee }} {% endfor %} @@ -61,12 +61,11 @@

    {% endif %} - - {%- if application_form_route -%} + {%- if doc.enable_admission_application -%}

    + href='/student-applicant?new=1&student_admission={{doc.name}}'> {{ _("Apply Now") }}

    {% endif %} diff --git a/erpnext/education/doctype/student_admission/test_student_admission.js b/erpnext/education/doctype/student_admission/test_student_admission.js index ed794b2482e..3a0bb0b2f23 100644 --- a/erpnext/education/doctype/student_admission/test_student_admission.js +++ b/erpnext/education/doctype/student_admission/test_student_admission.js @@ -11,7 +11,7 @@ QUnit.test('Test: Student Admission', function(assert) { {admission_start_date: '2016-04-20'}, {admission_end_date: '2016-05-31'}, {title: '2016-17 Admissions'}, - {application_form_route: 'student-applicant'}, + {enable_admission_application: 1}, {introduction: 'Test intro'}, {program_details: [ [ @@ -28,7 +28,7 @@ QUnit.test('Test: Student Admission', function(assert) { assert.ok(cur_frm.doc.admission_start_date == '2016-04-20'); assert.ok(cur_frm.doc.admission_end_date == '2016-05-31'); assert.ok(cur_frm.doc.title == '2016-17 Admissions'); - assert.ok(cur_frm.doc.application_form_route == 'student-applicant'); + assert.ok(cur_frm.doc.enable_admission_application == 1); assert.ok(cur_frm.doc.introduction == 'Test intro'); assert.ok(cur_frm.doc.program_details[0].program == 'Standard Test', 'Program correctly selected'); assert.ok(cur_frm.doc.program_details[0].application_fee == 1000); diff --git a/erpnext/education/doctype/student_admission_program/student_admission_program.json b/erpnext/education/doctype/student_admission_program/student_admission_program.json index 97b1bba4217..e9f041e101f 100644 --- a/erpnext/education/doctype/student_admission_program/student_admission_program.json +++ b/erpnext/education/doctype/student_admission_program/student_admission_program.json @@ -1,237 +1,77 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2017-09-15 12:59:43.207923", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2017-09-15 12:59:43.207923", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "program", + "min_age", + "max_age", + "column_break_4", + "application_fee", + "applicant_naming_series" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Program", - "length": 0, - "no_copy": 0, - "options": "Program", - "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": "program", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Program", + "options": "Program", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "minimum_age", - "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": "Minimum Age", - "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", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maximum_age", - "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": "Maximum Age", - "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": "application_fee", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Application Fee", + "show_days": 1, + "show_seconds": 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": "applicant_naming_series", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Naming Series (for Student Applicant)", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "application_fee", - "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": "Application Fee", - "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": "min_age", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Minimum Age", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "applicant_naming_series", - "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": "Naming Series (for Student Applicant)", - "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": "max_age", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Maximum Age", + "show_days": 1, + "show_seconds": 1 } - ], - "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": "2018-11-04 03:37:17.408427", - "modified_by": "Administrator", - "module": "Education", - "name": "Student Admission Program", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-06-10 23:06:30.037404", + "modified_by": "Administrator", + "module": "Education", + "name": "Student Admission Program", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "restrict_to_domain": "Education", + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/education/doctype/student_applicant/student_applicant.py b/erpnext/education/doctype/student_applicant/student_applicant.py index 6d0957c5021..8929abdc6cd 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant.py +++ b/erpnext/education/doctype/student_applicant/student_applicant.py @@ -6,7 +6,7 @@ from __future__ import print_function, unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import getdate +from frappe.utils import getdate, add_years, nowdate, date_diff class StudentApplicant(Document): def autoname(self): @@ -30,6 +30,7 @@ class StudentApplicant(Document): def validate(self): self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name])) + if self.student_admission and self.program and self.date_of_birth: self.validation_from_student_admission() @@ -43,16 +44,16 @@ class StudentApplicant(Document): frappe.throw(_("Please select Student Admission which is mandatory for the paid student applicant")) def validation_from_student_admission(self): + student_admission = get_student_admission_data(self.student_admission, self.program) - # different validation for minimum and maximum age so that either min/max can also work independently. - if student_admission and student_admission.minimum_age and \ - getdate(student_admission.minimum_age) < getdate(self.date_of_birth): - frappe.throw(_("Not eligible for the admission in this program as per DOB")) + if student_admission and student_admission.min_age and \ + date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.min_age)) < 0: + frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth")) - if student_admission and student_admission.maximum_age and \ - getdate(student_admission.maximum_age) > getdate(self.date_of_birth): - frappe.throw(_("Not eligible for the admission in this program as per DOB")) + if student_admission and student_admission.max_age and \ + date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.max_age)) > 0: + frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth")) def on_payment_authorized(self, *args, **kwargs): @@ -60,10 +61,12 @@ class StudentApplicant(Document): def get_student_admission_data(student_admission, program): + student_admission = frappe.db.sql("""select sa.admission_start_date, sa.admission_end_date, - sap.program, sap.minimum_age, sap.maximum_age, sap.applicant_naming_series + sap.program, sap.min_age, sap.max_age, sap.applicant_naming_series from `tabStudent Admission` sa, `tabStudent Admission Program` sap where sa.name = sap.parent and sa.name = %s and sap.program = %s""", (student_admission, program), as_dict=1) + if student_admission: return student_admission[0] else: diff --git a/erpnext/education/web_form/student_applicant/student_applicant.json b/erpnext/education/web_form/student_applicant/student_applicant.json index b1ad754c327..1810f07a054 100644 --- a/erpnext/education/web_form/student_applicant/student_applicant.json +++ b/erpnext/education/web_form/student_applicant/student_applicant.json @@ -1,200 +1,248 @@ { - "accept_payment": 0, - "allow_comments": 0, - "allow_delete": 0, - "allow_edit": 1, - "allow_incomplete": 0, - "allow_multiple": 1, - "allow_print": 0, - "amount": 0.0, - "amount_based_on_field": 0, - "creation": "2016-09-22 13:10:10.792735", - "doc_type": "Student Applicant", - "docstatus": 0, - "doctype": "Web Form", - "idx": 0, - "is_standard": 1, - "login_required": 1, - "max_attachment_size": 0, - "modified": "2017-02-21 05:44:46.022738", - "modified_by": "Administrator", - "module": "Education", - "name": "student-applicant", - "owner": "Administrator", - "payment_button_label": "Buy Now", - "published": 1, - "route": "student-applicant", - "show_sidebar": 1, - "sidebar_items": [], - "success_url": "/student-applicant", - "title": "Student Applicant", + "accept_payment": 0, + "allow_comments": 0, + "allow_delete": 0, + "allow_edit": 1, + "allow_incomplete": 0, + "allow_multiple": 1, + "allow_print": 0, + "amount": 0.0, + "amount_based_on_field": 0, + "creation": "2016-09-22 13:10:10.792735", + "doc_type": "Student Applicant", + "docstatus": 0, + "doctype": "Web Form", + "idx": 0, + "is_standard": 1, + "login_required": 1, + "max_attachment_size": 0, + "modified": "2020-06-11 22:53:45.875310", + "modified_by": "Administrator", + "module": "Education", + "name": "student-applicant", + "owner": "Administrator", + "payment_button_label": "Buy Now", + "published": 1, + "route": "student-applicant", + "route_to_success_link": 0, + "show_attachments": 0, + "show_in_grid": 0, + "show_sidebar": 1, + "sidebar_items": [], + "success_url": "/student-applicant", + "title": "Student Applicant", "web_form_fields": [ { - "fieldname": "first_name", - "fieldtype": "Data", - "hidden": 0, - "label": "First Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 1 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "first_name", + "fieldtype": "Data", + "hidden": 0, + "label": "First Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, { - "fieldname": "middle_name", - "fieldtype": "Data", - "hidden": 0, - "label": "Middle Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "middle_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Middle Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "last_name", - "fieldtype": "Data", - "hidden": 0, - "label": "Last Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "last_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Last Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "image", - "fieldtype": "Data", - "hidden": 0, - "label": "Image", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "image", + "fieldtype": "Data", + "hidden": 0, + "label": "Image", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "program", - "fieldtype": "Link", - "hidden": 0, - "label": "Program", - "max_length": 0, - "max_value": 0, - "options": "Program", - "read_only": 0, - "reqd": 1 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "program", + "fieldtype": "Link", + "hidden": 0, + "label": "Program", + "max_length": 0, + "max_value": 0, + "options": "Program", + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, { - "fieldname": "academic_year", - "fieldtype": "Link", - "hidden": 0, - "label": "Academic Year", - "max_length": 0, - "max_value": 0, - "options": "Academic Year", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "academic_year", + "fieldtype": "Link", + "hidden": 0, + "label": "Academic Year", + "max_length": 0, + "max_value": 0, + "options": "Academic Year", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "date_of_birth", - "fieldtype": "Date", - "hidden": 0, - "label": "Date of Birth", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "date_of_birth", + "fieldtype": "Date", + "hidden": 0, + "label": "Date of Birth", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "blood_group", - "fieldtype": "Select", - "hidden": 0, - "label": "Blood Group", - "max_length": 0, - "max_value": 0, - "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "blood_group", + "fieldtype": "Select", + "hidden": 0, + "label": "Blood Group", + "max_length": 0, + "max_value": 0, + "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "student_email_id", - "fieldtype": "Data", - "hidden": 0, - "label": "Student Email ID", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "student_email_id", + "fieldtype": "Data", + "hidden": 0, + "label": "Student Email ID", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "student_mobile_number", - "fieldtype": "Data", - "hidden": 0, - "label": "Student Mobile Number", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "student_mobile_number", + "fieldtype": "Data", + "hidden": 0, + "label": "Student Mobile Number", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "default": "INDIAN", - "fieldname": "nationality", - "fieldtype": "Data", - "hidden": 0, - "label": "Nationality", - "max_length": 0, - "max_value": 0, - "options": "", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "default": "INDIAN", + "fieldname": "nationality", + "fieldtype": "Data", + "hidden": 0, + "label": "Nationality", + "max_length": 0, + "max_value": 0, + "options": "", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "address_line_1", - "fieldtype": "Data", - "hidden": 0, - "label": "Address Line 1", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "address_line_1", + "fieldtype": "Data", + "hidden": 0, + "label": "Address Line 1", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "address_line_2", - "fieldtype": "Data", - "hidden": 0, - "label": "Address Line 2", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "address_line_2", + "fieldtype": "Data", + "hidden": 0, + "label": "Address Line 2", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "pincode", - "fieldtype": "Data", - "hidden": 0, - "label": "Pincode", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "pincode", + "fieldtype": "Data", + "hidden": 0, + "label": "Pincode", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "guardians", - "fieldtype": "Table", - "hidden": 0, - "label": "Guardians", - "max_length": 0, - "max_value": 0, - "options": "Student Guardian", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "guardians", + "fieldtype": "Table", + "hidden": 0, + "label": "Guardians", + "max_length": 0, + "max_value": 0, + "options": "Student Guardian", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "siblings", - "fieldtype": "Table", - "hidden": 0, - "label": "Siblings", - "max_length": 0, - "max_value": 0, - "options": "Student Sibling", - "read_only": 0, - "reqd": 0 + "allow_read_on_all_link_options": 0, + "fieldname": "siblings", + "fieldtype": "Table", + "hidden": 0, + "label": "Siblings", + "max_length": 0, + "max_value": 0, + "options": "Student Sibling", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "student_admission", + "fieldtype": "Link", + "hidden": 0, + "label": "Student Admission", + "max_length": 0, + "max_value": 0, + "options": "Student Admission", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 } ] } \ No newline at end of file diff --git a/erpnext/templates/generators/student_admission.html b/erpnext/templates/generators/student_admission.html index ae70df8b08b..8b153448eea 100644 --- a/erpnext/templates/generators/student_admission.html +++ b/erpnext/templates/generators/student_admission.html @@ -14,12 +14,12 @@ {%- if introduction -%}
    {{ introduction }}
    -{% endif %} +{% endif %} -{%- if application_form_route -%} +{%- if doc.enable_admission_application -%}

    + href='/student-applicant'> {{ _("Apply Now") }}

    {% endif %} From 7d035fac363be3a6b1d28c13a767cdbb227e5a8f Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 19 Jun 2020 17:28:58 +0530 Subject: [PATCH 686/929] Revert "fix: update remark on submitting payment entry" --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index a2e1a4a1612..1c5bea6cf15 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -452,6 +452,8 @@ class PaymentEntry(AccountsController): frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction")) def set_remarks(self): + if self.remarks: return + if self.payment_type=="Internal Transfer": remarks = [_("Amount {0} {1} transferred from {2} to {3}") .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)] From 368f5e29d26f3cf5dd240eeca9b893419cbb0071 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 20 Jun 2020 20:31:09 +0530 Subject: [PATCH 687/929] fix: Serial no / batch no Popup is coming for the non serialized items --- erpnext/public/js/controllers/transaction.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 445d683788e..e6e8ae7cb31 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -534,6 +534,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, () => me.frm.script_manager.trigger("price_list_rate", cdt, cdn), () => me.toggle_conversion_factor(item), + () => { + if (show_batch_dialog && !item.has_serial_no + && !item.has_batch_no) { + show_batch_dialog = false; + } + }, () => { if (show_batch_dialog) return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) From 0d95f56474e657d6a4750009a3b7d793cec0f419 Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 18:17:01 +0530 Subject: [PATCH 688/929] Skiping total row for tree-view reports (cherry picked from commit fbd66574ad267370f0955d9d2a59beb447fb59a6) --- .../report/sales_analytics/sales_analytics.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 97d9322918d..cb5d3eccfdc 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -23,7 +23,14 @@ class Analytics(object): self.get_columns() self.get_data() self.get_chart_data() - return self.columns, self.data, None, self.chart + + # Skiping total row for tree-view reports + skip_total_row = 0 + + if self.filters.tree_type in ["Supplier Group", "Item Group", "Customer Group", "Territory"]: + skip_total_row = 1 + + return self.columns, self.data, None, self.chart, None, skip_total_row def get_columns(self): self.columns = [{ @@ -194,9 +201,6 @@ class Analytics(object): def get_rows(self): self.data = [] self.get_periodic_data() - total_row = { - "entity": "Total", - } for entity, period_data in iteritems(self.entity_periodic_data): row = { @@ -210,9 +214,6 @@ class Analytics(object): row[scrub(period)] = amount total += amount - if not total_row.get(scrub(period)): total_row[scrub(period)] = 0 - total_row[scrub(period)] += amount - row["total"] = total if self.filters.tree_type == "Item": @@ -220,8 +221,6 @@ class Analytics(object): self.data.append(row) - self.data.append(total_row) - def get_rows_by_group(self): self.get_periodic_data() out = [] From a9ff40db67a79548ce9892f57308f1f393322d0d Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 18:19:06 +0530 Subject: [PATCH 689/929] fix: Skipping* (cherry picked from commit 65f00cea15d6d9e89a9123813c0f7fe72ee150c0) --- erpnext/selling/report/sales_analytics/sales_analytics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index cb5d3eccfdc..4d113c8e9e9 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -24,7 +24,7 @@ class Analytics(object): self.get_data() self.get_chart_data() - # Skiping total row for tree-view reports + # Skipping total row for tree-view reports skip_total_row = 0 if self.filters.tree_type in ["Supplier Group", "Item Group", "Customer Group", "Territory"]: From 41462f3c3695ed2f0c1e3d17bf47be2e575031fe Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 22:51:52 +0530 Subject: [PATCH 690/929] fix: test case (cherry picked from commit 3fbe6e9e4b2489f8f749497597e4eb32430577da) --- .../report/sales_analytics/test_analytics.py | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py index 7e8501dcc15..4d81a1e4dda 100644 --- a/erpnext/selling/report/sales_analytics/test_analytics.py +++ b/erpnext/selling/report/sales_analytics/test_analytics.py @@ -33,21 +33,6 @@ class TestAnalytics(unittest.TestCase): report = execute(filters) expected_data = [ - { - 'entity': 'Total', - 'apr_2017': 0.0, - 'may_2017': 0.0, - 'jun_2017': 2000.0, - 'jul_2017': 1000.0, - 'aug_2017': 0.0, - 'sep_2017': 1500.0, - 'oct_2017': 1000.0, - 'nov_2017': 0.0, - 'dec_2017': 0.0, - 'jan_2018': 0.0, - 'feb_2018': 2000.0, - 'mar_2018': 0.0 - }, { "entity": "_Test Customer 1", "entity_name": "_Test Customer 1", @@ -149,21 +134,6 @@ class TestAnalytics(unittest.TestCase): report = execute(filters) expected_data = [ - { - 'entity': 'Total', - 'apr_2017': 0.0, - 'may_2017': 0.0, - 'jun_2017': 20.0, - 'jul_2017': 10.0, - 'aug_2017': 0.0, - 'sep_2017': 15.0, - 'oct_2017': 10.0, - 'nov_2017': 0.0, - 'dec_2017': 0.0, - 'jan_2018': 0.0, - 'feb_2018': 20.0, - 'mar_2018': 0.0 - }, { "entity": "_Test Customer 1", "entity_name": "_Test Customer 1", From 8a2a32820a0196102cb45bc91777dbe88a8923da Mon Sep 17 00:00:00 2001 From: Afshan Date: Sun, 21 Jun 2020 17:48:44 +0530 Subject: [PATCH 691/929] fix: adding json for checked add total row (cherry picked from commit 84c69737421770163f96f712c72e1a2275b15267) --- erpnext/selling/report/sales_analytics/sales_analytics.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.json b/erpnext/selling/report/sales_analytics/sales_analytics.json index bf9edd6cd49..de5c3a6b2a7 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.json +++ b/erpnext/selling/report/sales_analytics/sales_analytics.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "creation": "2018-09-21 12:46:29.451048", "disable_prepared_report": 0, "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2020-04-30 19:49:02.303320", + "modified": "2020-06-19 17:41:03.132101", "modified_by": "Administrator", "module": "Selling", "name": "Sales Analytics", From b16b24fd10de4dd124d10c190df27ba297d0c6c8 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Mon, 22 Jun 2020 07:36:12 +0200 Subject: [PATCH 692/929] feat: Enabled translation on html files in LMS [Proposal] (#21582) * :fix: translation fix for html files in LMS * fix: typo in translation * Update content.html * Update content.html * Update profile.html Co-authored-by: Shivam Mishra Co-authored-by: Marica (cherry picked from commit 43be163b32a0f201389ca9d8e4c4918d49730530) --- erpnext/www/lms/content.html | 22 +++++++++++----------- erpnext/www/lms/course.html | 6 +++--- erpnext/www/lms/index.html | 2 +- erpnext/www/lms/macros/card.html | 4 ++-- erpnext/www/lms/macros/hero.html | 10 +++++----- erpnext/www/lms/profile.html | 8 ++++---- erpnext/www/lms/program.html | 6 +++--- erpnext/www/lms/topic.html | 10 +++++----- 8 files changed, 34 insertions(+), 34 deletions(-) diff --git a/erpnext/www/lms/content.html b/erpnext/www/lms/content.html index cdc71412c48..dc9b6d80fb5 100644 --- a/erpnext/www/lms/content.html +++ b/erpnext/www/lms/content.html @@ -59,7 +59,7 @@ {% macro title() %}
    @@ -69,15 +69,15 @@ {% macro navigation() %} {% if previous %} - Previous + {{_('Previous')}} {% else %} - Back to Course + {{ _('Back to Course') }} {% endif %} {% if next %} - + {% else %} - + {% endif %} {% endmacro %} @@ -86,7 +86,7 @@ {{ title() }}
    {% if content.duration %} - {{ content.duration }} Mins + {{ content.duration }} {{_('Mins')}} {% endif %} {% if content.publish_date and content.duration%} @@ -94,7 +94,7 @@ {% endif %} {% if content.publish_date %} - Published on {{ content.publish_date.strftime('%d, %b %Y') }} + {{_('Published on')}} {{ content.publish_date.strftime('%d, %b %Y') }} {% endif %}
    @@ -109,13 +109,13 @@ {{ title() }}
    {% if content.author or content.publish_date %} - Published + {{_('Published')}} {% endif %} {% if content.author %} - by {{ content.author }} + {{_('by')}} {{ content.author }} {% endif %} {% if content.publish_date %} - on {{ content.publish_date.strftime('%d, %b %Y') }} + {{_('on')}} {{ content.publish_date.strftime('%d, %b %Y') }} {% endif %}
    @@ -205,4 +205,4 @@ {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/lms/course.html b/erpnext/www/lms/course.html index f2fd9363e8c..0d70ed5cefd 100644 --- a/erpnext/www/lms/course.html +++ b/erpnext/www/lms/course.html @@ -72,11 +72,11 @@ {% if has_access %} diff --git a/erpnext/www/lms/index.html b/erpnext/www/lms/index.html index ffb4419f367..7ce3521273f 100644 --- a/erpnext/www/lms/index.html +++ b/erpnext/www/lms/index.html @@ -45,7 +45,7 @@

    {{ education_settings.description }}

    {% if frappe.session.user == 'Guest' %} - Sign Up + {{_('Sign Up')}} {% endif %}

    diff --git a/erpnext/www/lms/macros/card.html b/erpnext/www/lms/macros/card.html index 076061d41b3..dc8fc5c72c7 100644 --- a/erpnext/www/lms/macros/card.html +++ b/erpnext/www/lms/macros/card.html @@ -15,8 +15,8 @@
    {% if has_access or program.intro_video%} {% endif %} diff --git a/erpnext/www/lms/macros/hero.html b/erpnext/www/lms/macros/hero.html index 66bb861c467..94f239eb8ed 100644 --- a/erpnext/www/lms/macros/hero.html +++ b/erpnext/www/lms/macros/hero.html @@ -2,16 +2,16 @@

    {{ title }}

    {{ description or ''}}

    {% if frappe.session.user == 'Guest' %} - Sign Up + {{_('Sign Up')}} {% elif not has_access %} - + {% endif %}

    @@ -28,7 +28,7 @@ let btn = document.getElementById('enroll'); btn.disbaled = true; - btn.innerText = 'Enrolling...' + btn.innerText = __('Enrolling...') let opts = { method: 'erpnext.education.utils.enroll_in_program', @@ -44,7 +44,7 @@ window.location.reload() } }) - success_dialog.set_message('You have successfully enrolled for the program '); + success_dialog.set_message(__('You have successfully enrolled for the program ')); success_dialog.$message.show() success_dialog.show(); btn.disbaled = false; diff --git a/erpnext/www/lms/profile.html b/erpnext/www/lms/profile.html index 9508daedb71..5755dfe6d8e 100644 --- a/erpnext/www/lms/profile.html +++ b/erpnext/www/lms/profile.html @@ -30,7 +30,7 @@
    @@ -43,11 +43,11 @@

    {{ student.first_name }} {{ student.last_name or '' }}

    @@ -61,4 +61,4 @@
    -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/lms/program.html b/erpnext/www/lms/program.html index 271b7813bb4..7ad618630a4 100644 --- a/erpnext/www/lms/program.html +++ b/erpnext/www/lms/program.html @@ -55,11 +55,11 @@ {% if has_access and progress[course.name] %} {% endif %} diff --git a/erpnext/www/lms/topic.html b/erpnext/www/lms/topic.html index 1f0d1876646..cd24616cd45 100644 --- a/erpnext/www/lms/topic.html +++ b/erpnext/www/lms/topic.html @@ -23,13 +23,13 @@ {% if has_access %} From aa22cca67a22b48ca22c3269f9ca1ff68788450c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 15 Jun 2020 12:07:04 +0530 Subject: [PATCH 693/929] fix: Minor fixes in cost center (cherry picked from commit 817cbc4b48f7d00bcbfe84c6e2b600e355cce8db) --- erpnext/accounts/doctype/cost_center/cost_center.js | 12 +++++++++--- .../accounts/doctype/cost_center/cost_center.json | 2 +- erpnext/accounts/utils.py | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/cost_center/cost_center.js b/erpnext/accounts/doctype/cost_center/cost_center.js index 9e2f6eed3b6..0672bf1910e 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.js +++ b/erpnext/accounts/doctype/cost_center/cost_center.js @@ -60,8 +60,13 @@ frappe.ui.form.on('Cost Center', { "label": "Cost Center Number", "fieldname": "cost_center_number", "fieldtype": "Data", - "reqd": 1, "default": frm.doc.cost_center_number + }, + { + "label": __("Merge with existing"), + "fieldname": "merge", + "fieldtype": "Check", + "default": 0 } ], primary_action: function() { @@ -76,8 +81,9 @@ frappe.ui.form.on('Cost Center', { args: { docname: frm.doc.name, cost_center_name: data.cost_center_name, - cost_center_number: data.cost_center_number, - company: frm.doc.company + cost_center_number: cstr(data.cost_center_number), + company: frm.doc.company, + merge: data.merge }, callback: function(r) { frappe.dom.unfreeze(); diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index 5013c92a327..fa2fb51d061 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -125,7 +125,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-04-29 16:09:30.025214", + "modified": "2020-06-12 16:09:30.025214", "modified_by": "Administrator", "module": "Accounts", "name": "Cost Center", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index d1aa4011b63..e093e1d668c 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -817,7 +817,7 @@ def create_payment_gateway_account(gateway): pass @frappe.whitelist() -def update_cost_center(docname, cost_center_name, cost_center_number, company): +def update_cost_center(docname, cost_center_name, cost_center_number, company, merge): ''' Renames the document by adding the number as a prefix to the current name and updates all transaction where it was present. @@ -833,7 +833,7 @@ def update_cost_center(docname, cost_center_name, cost_center_number, company): new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company) if docname != new_name: - frappe.rename_doc("Cost Center", docname, new_name, force=1) + frappe.rename_doc("Cost Center", docname, new_name, force=1, merge=merge) return new_name def validate_field_number(doctype_name, docname, number_value, company, field_name): From f2fe8053dc2df2eb63460db660e2f3697a58b840 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 15 Jun 2020 12:23:24 +0530 Subject: [PATCH 694/929] fix: Validation for group cost center (cherry picked from commit 0d880079b197c15374a7fe81f543d58be70ae24c) --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 7358a31469b..4caf47ff39e 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _ -from frappe.utils import flt, fmt_money, getdate, formatdate +from frappe.utils import flt, fmt_money, getdate, formatdate, cint from frappe.model.document import Document from frappe.model.naming import set_name_from_naming_options from frappe.model.meta import get_field_precision @@ -137,10 +137,17 @@ class GLEntry(Document): return self.cost_center_company[self.cost_center] + def _check_is_group(): + return cint(frappe.get_cached_value('Cost Center', self.cost_center, 'is_group')) + if self.cost_center and _get_cost_center_company() != self.company: frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}") .format(self.voucher_type, self.voucher_no, self.cost_center, self.company)) + if self.cost_center and _check_is_group(): + frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot + be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center))) + def validate_party(self): validate_party_frozen_disabled(self.party_type, self.party) From cfe9ed0dfa18b7f5144c19be336a86aab6f21bbf Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 20 Jun 2020 12:32:30 +0530 Subject: [PATCH 695/929] fix: Test (cherry picked from commit bef80b7981b721f470a15b95bc5831d438b50132) --- .../account_balance/test_account_balance.py | 2 +- .../test_accounts_receivable.py | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/report/account_balance/test_account_balance.py b/erpnext/accounts/report/account_balance/test_account_balance.py index 5544fc46738..b6ced312d09 100644 --- a/erpnext/accounts/report/account_balance/test_account_balance.py +++ b/erpnext/accounts/report/account_balance/test_account_balance.py @@ -61,7 +61,7 @@ def make_sales_invoice(): debit_to = 'Debtors - _TC2', income_account = 'Sales - _TC2', expense_account = 'Cost of Goods Sold - _TC2', - cost_center = '_Test Company 2 - _TC2') + cost_center = 'Main - _TC2') diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index f0274b44723..2ff5b531c51 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -63,7 +63,7 @@ def make_sales_invoice(): debit_to = 'Debtors - _TC2', income_account = 'Sales - _TC2', expense_account = 'Cost of Goods Sold - _TC2', - cost_center = '_Test Company 2 - _TC2', + cost_center = 'Main - _TC2', do_not_save=1) si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30)) @@ -83,14 +83,14 @@ def make_payment(docname): def make_credit_note(docname): create_sales_invoice(company="_Test Company 2", - customer = '_Test Customer 2', - currency = 'EUR', - qty = -1, - warehouse = 'Finished Goods - _TC2', - debit_to = 'Debtors - _TC2', - income_account = 'Sales - _TC2', - expense_account = 'Cost of Goods Sold - _TC2', - cost_center = '_Test Company 2 - _TC2', - is_return = 1, - return_against = docname) + customer = '_Test Customer 2', + currency = 'EUR', + qty = -1, + warehouse = 'Finished Goods - _TC2', + debit_to = 'Debtors - _TC2', + income_account = 'Sales - _TC2', + expense_account = 'Cost of Goods Sold - _TC2', + cost_center = 'Main - _TC2', + is_return = 1, + return_against = docname) From e5ffe3fea4c42696767f27217786b15cf0a24ffa Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 22 Jun 2020 12:36:23 +0530 Subject: [PATCH 696/929] fix: completed qty not updated in work order --- erpnext/manufacturing/doctype/job_card/job_card.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 8fce3161375..88b20eb6942 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -131,7 +131,7 @@ class JobCard(Document): work_order_field = "name" if field == "operation_id" else field for data in wo.operations: - if data.get(work_order_field) == self.get(field) and data.workstation == self.workstation: + if data.get(work_order_field) == self.get(field): data.completed_qty = for_quantity data.actual_operation_time = time_in_mins data.actual_start_time = time_data[0].start_time if time_data else None From ebbbc85fc0f9aa418163f8344d3a234ecbe08488 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 22 Jun 2020 13:44:11 +0530 Subject: [PATCH 697/929] added validation and clear fields for Apply Rule On Other --- .../doctype/pricing_rule/pricing_rule.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index a48aab119cc..87888e4febe 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -17,6 +17,8 @@ from six import string_types apply_on_dict = {"Item Code": "items", "Item Group": "item_groups", "Brand": "brands"} +other_fields = ["other_item_code", "other_item_group", "other_brand"] + class PricingRule(Document): def validate(self): self.validate_mandatory() @@ -51,6 +53,13 @@ class PricingRule(Document): if tocheck and not self.get(tocheck): throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError) + if self.apply_rule_on_other: + o_field = 'other_' + frappe.scrub(self.apply_rule_on_other) + if not self.get(o_field) and o_field in other_fields: + frappe.throw(_("For the 'Apply Rule On Other' condition the field {0} is mandatory") + .format(frappe.bold(self.apply_rule_on_other))) + + if self.price_or_product_discount == 'Price' and not self.rate_or_discount: throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError) @@ -97,6 +106,14 @@ class PricingRule(Document): if self.mixed_conditions and self.get("same_item"): self.same_item = 0 + apply_rule_on_other = frappe.scrub(self.apply_rule_on_other or "") + + cleanup_other_fields = (other_fields if not apply_rule_on_other + else [o_field for o_field in other_fields if o_field != 'other_' + apply_rule_on_other]) + + for other_field in cleanup_other_fields: + self.set(other_field, None) + def validate_rate_or_discount(self): for field in ["Rate"]: if flt(self.get(frappe.scrub(field))) < 0: @@ -254,7 +271,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa if pricing_rule.coupon_code_based==1 and args.coupon_code==None: return item_details - + if not pricing_rule.validate_applied_rule: if pricing_rule.price_or_product_discount == "Price": apply_price_discount_rule(pricing_rule, item_details, args) From 1039079e3798a6f52b26012e57bffa88326376f8 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 22 Jun 2020 17:25:53 +0530 Subject: [PATCH 698/929] fix: set cost center in child table (#22373) --- erpnext/hr/doctype/expense_claim/expense_claim.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 88bd689d9de..eeb56db804a 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -221,7 +221,6 @@ frappe.ui.form.on("Expense Claim", { }, update_employee_advance_claimed_amount: function(frm) { - console.log("update_employee_advance_claimed_amount") let amount_to_be_allocated = frm.doc.grand_total; $.each(frm.doc.advances || [], function(i, advance){ if (amount_to_be_allocated >= advance.unclaimed_amount){ @@ -297,6 +296,16 @@ frappe.ui.form.on("Expense Claim", { frm.events.get_advances(frm); }, + cost_center: function(frm) { + frm.events.set_child_cost_center(frm); + }, + set_child_cost_center: function(frm){ + (frm.doc.expenses || []).forEach(function(d) { + if (!d.cost_center){ + d.cost_center = frm.doc.cost_center; + } + }); + }, get_taxes: function(frm) { if(frm.doc.taxes) { frappe.call({ @@ -339,6 +348,9 @@ frappe.ui.form.on("Expense Claim", { }); frappe.ui.form.on("Expense Claim Detail", { + expenses_add: function(frm, cdt, cdn) { + frm.events.set_child_cost_center(frm); + }, amount: function(frm, cdt, cdn) { var child = locals[cdt][cdn]; frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.amount); From 413c2b229a929ea6878c346e3f577443b94bfbf5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2020 17:26:42 +0530 Subject: [PATCH 699/929] fix: 'Last Purchase Rate' taking wrong on BOM (#20689) (#22370) * fix: 'Last Purchase Rate' taking wrong on BOM. #20228 * fix: Added condition for None purchase order and purchase receipt (#20689) * fix: fetch last purchase rate Co-authored-by: Nabin Hait (cherry picked from commit 2866617c82dcec39779d6e286cb462540c2728a5) Co-authored-by: Lokesh Waingankar <59611773+lokesh-indictrans@users.noreply.github.com> --- erpnext/buying/utils.py | 3 +-- erpnext/stock/doctype/item/item.py | 14 +++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/erpnext/buying/utils.py b/erpnext/buying/utils.py index b5598f8d0b2..47b48665b60 100644 --- a/erpnext/buying/utils.py +++ b/erpnext/buying/utils.py @@ -12,7 +12,6 @@ from erpnext.stock.doctype.item.item import validate_end_of_life def update_last_purchase_rate(doc, is_submit): """updates last_purchase_rate in item table for each item""" - import frappe.utils this_purchase_date = frappe.utils.getdate(doc.get('posting_date') or doc.get('transaction_date')) @@ -23,7 +22,7 @@ def update_last_purchase_rate(doc, is_submit): # compare last purchase date and this transaction's date last_purchase_rate = None if last_purchase_details and \ - (last_purchase_details.purchase_date > this_purchase_date): + (doc.get('docstatus') == 2 or last_purchase_details.purchase_date > this_purchase_date): last_purchase_rate = last_purchase_details['base_net_rate'] elif is_submit == 1: # even if this transaction is the latest one, it should be submitted diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 0cc8b03ab49..ec283461ef8 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -954,6 +954,7 @@ def _msgprint(msg, verbose): def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): """returns last purchase details in stock uom""" # get last purchase order item details + last_purchase_order = frappe.db.sql("""\ select po.name, po.transaction_date, po.conversion_rate, po_item.conversion_factor, po_item.base_price_list_rate, @@ -964,6 +965,7 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): order by po.transaction_date desc, po.name desc limit 1""", (item_code, cstr(doc_name)), as_dict=1) + # get last purchase receipt item details last_purchase_receipt = frappe.db.sql("""\ select pr.name, pr.posting_date, pr.posting_time, pr.conversion_rate, @@ -975,19 +977,20 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): order by pr.posting_date desc, pr.posting_time desc, pr.name desc limit 1""", (item_code, cstr(doc_name)), as_dict=1) + + purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date or "1900-01-01") purchase_receipt_date = getdate(last_purchase_receipt and last_purchase_receipt[0].posting_date or "1900-01-01") - if (purchase_order_date > purchase_receipt_date) or \ - (last_purchase_order and not last_purchase_receipt): + if last_purchase_order and (purchase_order_date >= purchase_receipt_date or not last_purchase_receipt): # use purchase order + last_purchase = last_purchase_order[0] purchase_date = purchase_order_date - elif (purchase_receipt_date > purchase_order_date) or \ - (last_purchase_receipt and not last_purchase_order): + elif last_purchase_receipt and (purchase_receipt_date > purchase_order_date or not last_purchase_order): # use purchase receipt last_purchase = last_purchase_receipt[0] purchase_date = purchase_receipt_date @@ -999,10 +1002,11 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): out = frappe._dict({ "base_price_list_rate": flt(last_purchase.base_price_list_rate) / conversion_factor, "base_rate": flt(last_purchase.base_rate) / conversion_factor, - "base_net_rate": flt(last_purchase.net_rate) / conversion_factor, + "base_net_rate": flt(last_purchase.base_net_rate) / conversion_factor, "discount_percentage": flt(last_purchase.discount_percentage), "purchase_date": purchase_date }) + conversion_rate = flt(conversion_rate) or 1.0 out.update({ From e220be541556ea6d86a46a0c749943d4b94c883c Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com> Date: Mon, 22 Jun 2020 17:33:04 +0530 Subject: [PATCH 700/929] fix: Batch No label in Purchase Receipt (#22360) --- .../doctype/purchase_receipt_item/purchase_receipt_item.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index e1dc95be481..095a712c1c1 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -551,7 +551,7 @@ "fieldname": "batch_no", "fieldtype": "Link", "in_list_view": 1, - "label": "Batch No!", + "label": "Batch No", "no_copy": 1, "oldfieldname": "batch_no", "oldfieldtype": "Link", @@ -821,8 +821,7 @@ ], "idx": 1, "istable": 1, - "links": [], - "modified": "2020-04-07 18:38:21.141558", + "modified": "2020-06-20 18:49:16.824489", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From 06ca3b511212ab3300443c58798eeb142c33c3c5 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 22 Jun 2020 18:04:48 +0530 Subject: [PATCH 701/929] Purchase receipt to invoice hotfix (#22146) * fix: Handle rows with same item code from Purchase Receipt to Invoice. * fix: Handle rows with same item code from Purchase Receipt to Invoice. * fix: Added patch, fixed tests, fixed delivery note behaviour * chore: Added comments amd fixed typo * fix: Added patch to patches.txt * fix: Patch fix and simplification, json timestamp updation. * fix: Set Purchase Receipt and Delivery Note detail patch (#21607) * fix: Set Purchase Receipt and Delievry Note detail patch * fix: Reload purchase receipt item * fix(set_purchase_receipt_delivery_note_detail): commit after every 100 sql updates (#22016) Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Co-authored-by: sahil28297 <37302950+sahil28297@users.noreply.github.com> Co-authored-by: Nabin Hait --- .../controllers/sales_and_purchase_return.py | 5 +- erpnext/patches.txt | 1 + ...t_purchase_receipt_delivery_note_detail.py | 92 +++++++++++++++++++ .../doctype/delivery_note/delivery_note.py | 11 +-- .../delivery_note/test_delivery_note.py | 5 +- .../delivery_note_item.json | 12 ++- .../purchase_receipt/purchase_receipt.py | 9 +- .../purchase_receipt/test_purchase_receipt.py | 5 +- .../purchase_receipt_item.json | 11 +++ 9 files changed, 136 insertions(+), 15 deletions(-) create mode 100644 erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 81fdbbefc35..90c67f1e521 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -74,7 +74,7 @@ def validate_returned_items(doc): for d in doc.get("items"): if d.item_code and (flt(d.qty) < 0 or flt(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}") + frappe.throw(_("Row # {0}: Returned Item {1} does not exist in {2} {3}") .format(d.idx, d.item_code, doc.doctype, doc.return_against)) else: ref = valid_items.get(d.item_code, frappe._dict()) @@ -266,6 +266,8 @@ def make_return_doc(doctype, source_name, target_doc=None): target_doc.purchase_order = source_doc.purchase_order target_doc.purchase_order_item = source_doc.purchase_order_item target_doc.rejected_warehouse = source_doc.rejected_warehouse + target_doc.purchase_receipt_item = source_doc.name + elif doctype == "Purchase Invoice": target_doc.received_qty = -1* source_doc.received_qty target_doc.rejected_qty = -1* source_doc.rejected_qty @@ -282,6 +284,7 @@ def make_return_doc(doctype, source_name, target_doc=None): target_doc.so_detail = source_doc.so_detail target_doc.si_detail = source_doc.si_detail target_doc.expense_account = source_doc.expense_account + target_doc.dn_detail = source_doc.name if default_warehouse_for_sales_return: target_doc.warehouse = default_warehouse_for_sales_return elif doctype == "Sales Invoice": diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0f865760ca9..994d6ca5bee 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -652,6 +652,7 @@ erpnext.patches.v12_0.update_price_or_product_discount erpnext.patches.v12_0.add_export_type_field_in_party_master erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22 erpnext.patches.v12_0.create_irs_1099_field_united_states +erpnext.patches.v12_0.set_purchase_receipt_delivery_note_detail erpnext.patches.v12_0.add_permission_in_lower_deduction erpnext.patches.v12_0.set_permission_einvoicing erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom diff --git a/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py new file mode 100644 index 00000000000..52c9a2d7b3c --- /dev/null +++ b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py @@ -0,0 +1,92 @@ +from __future__ import unicode_literals +import frappe +from collections import defaultdict + +def execute(): + + frappe.reload_doc('stock', 'doctype', 'delivery_note_item', force=True) + frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item', force=True) + + def map_rows(doc_row, return_doc_row, detail_field, doctype): + """Map rows after identifying similar ones.""" + + frappe.db.sql(""" UPDATE `tab{doctype} Item` set {detail_field} = '{doc_row_name}' + where name = '{return_doc_row_name}'""" \ + .format(doctype=doctype, + detail_field=detail_field, + doc_row_name=doc_row.get('name'), + return_doc_row_name=return_doc_row.get('name'))) #nosec + + def row_is_mappable(doc_row, return_doc_row, detail_field): + """Checks if two rows are similar enough to be mapped.""" + + if doc_row.item_code == return_doc_row.item_code and not return_doc_row.get(detail_field): + if doc_row.get('batch_no') and return_doc_row.get('batch_no') and doc_row.batch_no == return_doc_row.batch_no: + return True + + elif doc_row.get('serial_no') and return_doc_row.get('serial_no'): + doc_sn = doc_row.serial_no.split('\n') + return_doc_sn = return_doc_row.serial_no.split('\n') + + if set(doc_sn) & set(return_doc_sn): + # if two rows have serial nos in common, map them + return True + + elif doc_row.rate == return_doc_row.rate: + return True + else: + return False + + def make_return_document_map(doctype, return_document_map): + """Returns a map of documents and it's return documents. + Format => { 'document' : ['return_document_1','return_document_2'] }""" + + return_against_documents = frappe.db.sql(""" + SELECT + return_against as document, name as return_document + FROM `tab{doctype}` + WHERE + is_return = 1 and docstatus = 1""".format(doctype=doctype),as_dict=1) #nosec + + for entry in return_against_documents: + return_document_map[entry.document].append(entry.return_document) + + return return_document_map + + def set_document_detail_in_return_document(doctype): + """Map each row of the original document in the return document.""" + mapped = [] + return_document_map = defaultdict(list) + detail_field = "purchase_receipt_item" if doctype=="Purchase Receipt" else "dn_detail" + + child_doc = frappe.scrub("{0} Item".format(doctype)) + frappe.reload_doc("stock", "doctype", child_doc) + + return_document_map = make_return_document_map(doctype, return_document_map) + + count = 0 + + #iterate through original documents and its return documents + for docname in return_document_map: + doc_items = frappe.get_cached_doc(doctype, docname).get("items") + for return_doc in return_document_map[docname]: + return_doc_items = frappe.get_cached_doc(doctype, return_doc).get("items") + + #iterate through return document items and original document items for mapping + for return_item in return_doc_items: + for doc_item in doc_items: + if row_is_mappable(doc_item, return_item, detail_field) and doc_item.get('name') not in mapped: + map_rows(doc_item, return_item, detail_field, doctype) + mapped.append(doc_item.get('name')) + break + else: + continue + + # commit after every 100 sql updates + count += 1 + if count%100 == 0: + frappe.db.commit() + + set_document_detail_in_return_document("Purchase Receipt") + set_document_detail_in_return_document("Delivery Note") + frappe.db.commit() diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index c72bb893fff..f4334c4980a 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -386,13 +386,12 @@ def get_invoiced_qty_map(delivery_note): def get_returned_qty_map(delivery_note): """returns a map: {so_detail: returned_qty}""" - returned_qty_map = frappe._dict(frappe.db.sql("""select dn_item.item_code, sum(abs(dn_item.qty)) as qty + returned_qty_map = frappe._dict(frappe.db.sql("""select dn_item.dn_detail, abs(dn_item.qty) as qty from `tabDelivery Note Item` dn_item, `tabDelivery Note` dn where dn.name = dn_item.parent and dn.docstatus = 1 and dn.is_return = 1 and dn.return_against = %s - group by dn_item.item_code """, delivery_note)) return returned_qty_map @@ -411,7 +410,7 @@ def make_sales_invoice(source_name, target_doc=None): target.run_method("set_po_nos") if len(target.get("items")) == 0: - frappe.throw(_("All these items have already been invoiced")) + frappe.throw(_("All these items have already been Invoiced/Returned")) target.run_method("calculate_taxes_and_totals") @@ -436,9 +435,9 @@ def make_sales_invoice(source_name, target_doc=None): pending_qty = item_row.qty - invoiced_qty_map.get(item_row.name, 0) returned_qty = 0 - if returned_qty_map.get(item_row.item_code, 0) > 0: - returned_qty = flt(returned_qty_map.get(item_row.item_code, 0)) - returned_qty_map[item_row.item_code] -= pending_qty + if returned_qty_map.get(item_row.name, 0) > 0: + returned_qty = flt(returned_qty_map.get(item_row.name, 0)) + returned_qty_map[item_row.name] -= pending_qty if returned_qty: if returned_qty >= pending_qty: diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index d7a93fb6917..cad822b369e 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -623,6 +623,7 @@ class TestDeliveryNote(unittest.TestCase): dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-1, do_not_submit=True) dn1.items[0].against_sales_order = so.name dn1.items[0].so_detail = so.items[0].name + dn1.items[0].dn_detail = dn.items[0].name dn1.submit() si = make_sales_invoice(dn.name) @@ -649,7 +650,9 @@ class TestDeliveryNote(unittest.TestCase): si1.save() si1.submit() - create_delivery_note(is_return=1, return_against=dn.name, qty=-2) + dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-2, do_not_submit=True) + dn1.items[0].dn_detail = dn.items[0].name + dn1.submit() si2 = make_sales_invoice(dn.name) self.assertEquals(si2.items[0].qty, 2) diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index a3386fce193..6e2adc3ac97 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -67,6 +67,7 @@ "so_detail", "against_sales_invoice", "si_detail", + "dn_detail", "section_break_40", "batch_no", "serial_no", @@ -715,12 +716,21 @@ "fieldname": "reason_for_return_section_break", "fieldtype": "Section Break", "label": "Reason For Return" + }, + { + "fieldname": "dn_detail", + "fieldtype": "Data", + "hidden": 1, + "label": "Against Delivery Note Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-06 14:18:33.131672", + "modified": "2020-04-28 14:18:33.131672", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index f21c4ef8593..a150e097d42 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -495,7 +495,7 @@ def make_purchase_invoice(source_name, target_doc=None): def set_missing_values(source, target): if len(target.get("items")) == 0: - frappe.throw(_("All items have already been invoiced")) + frappe.throw(_("All items have already been Invoiced/Returned")) doc = frappe.get_doc(target) doc.ignore_pricing_rule = 1 @@ -505,11 +505,11 @@ def make_purchase_invoice(source_name, target_doc=None): def update_item(source_doc, target_doc, source_parent): target_doc.qty, returned_qty = get_pending_qty(source_doc) - returned_qty_map[source_doc.item_code] = returned_qty + returned_qty_map[source_doc.name] = returned_qty def get_pending_qty(item_row): pending_qty = item_row.qty - invoiced_qty_map.get(item_row.name, 0) - returned_qty = flt(returned_qty_map.get(item_row.item_code, 0)) + returned_qty = flt(returned_qty_map.get(item_row.name, 0)) if returned_qty: if returned_qty >= pending_qty: pending_qty = 0 @@ -567,13 +567,12 @@ def get_invoiced_qty_map(purchase_receipt): def get_returned_qty_map(purchase_receipt): """returns a map: {so_detail: returned_qty}""" - returned_qty_map = frappe._dict(frappe.db.sql("""select pr_item.item_code, sum(abs(pr_item.qty)) as qty + returned_qty_map = frappe._dict(frappe.db.sql("""select pr_item.purchase_receipt_item, abs(pr_item.qty) as qty from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr where pr.name = pr_item.parent and pr.docstatus = 1 and pr.is_return = 1 and pr.return_against = %s - group by pr_item.item_code """, purchase_receipt)) return returned_qty_map diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 09271cebfa8..65ff2de0b49 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -474,6 +474,7 @@ class TestPurchaseReceipt(unittest.TestCase): pr1 = make_purchase_receipt(is_return=1, return_against=pr.name, qty=-1, do_not_submit=True) pr1.items[0].purchase_order = po.name pr1.items[0].purchase_order_item = po.items[0].name + pr1.items[0].purchase_receipt_item = pr.items[0].name pr1.submit() pi = make_purchase_invoice(pr.name) @@ -497,7 +498,9 @@ class TestPurchaseReceipt(unittest.TestCase): pi1.save() pi1.submit() - make_purchase_receipt(is_return=1, return_against=pr1.name, qty=-2) + pr2 = make_purchase_receipt(is_return=1, return_against=pr1.name, qty=-2, do_not_submit=True) + pr2.items[0].purchase_receipt_item = pr1.items[0].name + pr2.submit() pi2 = make_purchase_invoice(pr1.name) self.assertEquals(pi2.items[0].qty, 2) diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 095a712c1c1..3d46289d570 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -78,6 +78,7 @@ "stock_qty", "purchase_order_item", "material_request_item", + "purchase_receipt_item", "section_break_45", "allow_zero_valuation_rate", "bom", @@ -817,10 +818,20 @@ "label": "Asset Category", "options": "Asset Category", "read_only": 1 + }, + { + "fieldname": "purchase_receipt_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Purchase Receipt Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, + "links": [], "modified": "2020-06-20 18:49:16.824489", "modified_by": "Administrator", "module": "Stock", From 171699537c3e7d7632f45123c6d30012c8de7281 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 22 Jun 2020 20:48:28 +0530 Subject: [PATCH 702/929] fix: alternative item not working for subcontract --- erpnext/stock/doctype/stock_entry/stock_entry.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index abc4fd0b377..d95a30949d9 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -571,9 +571,7 @@ class StockEntry(StockController): {"parent": self.purchase_order, "item_code": se_item.subcontracted_item}, "bom") - allow_alternative_item = frappe.get_value("BOM", bom_no, "allow_alternative_item") - - if allow_alternative_item: + if se_item.allow_alternative_item: original_item_code = frappe.get_value("Item Alternative", {"alternative_item_code": item_code}, "item_code") required_qty = sum([flt(d.required_qty) for d in purchase_order.supplied_items \ @@ -736,7 +734,7 @@ class StockEntry(StockController): def get_item_details(self, args=None, for_update=False): item = frappe.db.sql("""select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group, - i.has_batch_no, i.sample_quantity, i.has_serial_no, + i.has_batch_no, i.sample_quantity, i.has_serial_no, i.allow_alternative_item, id.expense_account, id.buying_cost_center from `tabItem` i LEFT JOIN `tabItem Default` id ON i.name=id.parent and id.company=%s where i.name=%s @@ -770,6 +768,9 @@ class StockEntry(StockController): 'sample_quantity' : item.sample_quantity }) + if self.purpose == 'Send to Subcontractor': + ret["allow_alternative_item"] = item.allow_alternative_item + # update uom if args.get("uom") and for_update: ret.update(get_uom_details(args.get('item_code'), args.get('uom'), args.get('qty'))) From 21a75eb2ba45dcfaf838b934f4486c339456afa6 Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 14:36:56 +0530 Subject: [PATCH 703/929] fix: set row_id by default as previous row for On Previous Row Amount and On Previous Row Total --- erpnext/public/js/controllers/taxes_and_totals.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 28fb6490254..46bb3d3540c 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -329,6 +329,17 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ var tax_rate = this._get_tax_rate(tax, item_tax_map); var current_tax_amount = 0.0; + // To set row_id by default as previous row. + if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) { + if (tax.idx == 1) { + frappe.throw( + __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")) + } + if (!tax.row_id) { + tax.row_id = tax.idx - 1; + } + } + if(tax.charge_type == "Actual") { // distribute the tax amount proportionally to each item row var actual = flt(tax.tax_amount, precision("tax_amount", tax)); From 7e04dcae0a91eb55d5d3a4dcda17b8408f9ce1fe Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 15:10:15 +0530 Subject: [PATCH 704/929] style: formate according to Codacy/PR Quality Review --- erpnext/public/js/controllers/taxes_and_totals.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 46bb3d3540c..0b3a01db97a 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -331,9 +331,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ // To set row_id by default as previous row. if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) { - if (tax.idx == 1) { - frappe.throw( - __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")) + if (tax.idx === 1) { + frappe.throw( + __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")); } if (!tax.row_id) { tax.row_id = tax.idx - 1; From def13f14a362a82f4598fef11725e381643a184b Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 16:22:41 +0530 Subject: [PATCH 705/929] style: formate according to Codacy/PR Quality Review --- erpnext/public/js/controllers/taxes_and_totals.js | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 0b3a01db97a..9518ba9dd51 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -339,7 +339,6 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ tax.row_id = tax.idx - 1; } } - if(tax.charge_type == "Actual") { // distribute the tax amount proportionally to each item row var actual = flt(tax.tax_amount, precision("tax_amount", tax)); From 8a1f2ed65a0ca9e925ea32f4b7d07add1f3c0b72 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Mon, 22 Jun 2020 22:40:04 +0530 Subject: [PATCH 706/929] fix:status error in purchase invoice --- .../accounts/doctype/purchase_invoice/purchase_invoice_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js index 800ed921bdf..4f751636b69 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js @@ -16,7 +16,7 @@ frappe.listview_settings['Purchase Invoice'] = { } else if(frappe.datetime.get_diff(doc.due_date) < 0) { return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"]; } else { - return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"]; + return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"]; } } else if(cint(doc.is_return)) { return [__("Return"), "darkgrey", "is_return,=,Yes"]; From 578bdff141ad98456e30f6e97eb4f67c89c9a066 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 23 Jun 2020 10:34:52 +0530 Subject: [PATCH 707/929] fix: offline pos not working for special character item (#22391) --- erpnext/accounts/page/pos/pos.js | 33 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 28c9149c561..279ae25bf71 100755 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -1062,7 +1062,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ } if(index < me.page_len) { $(frappe.render_template("pos_item", { - item_code: obj.name, + item_code: escape(obj.name), item_price: item_price, item_name: obj.name === obj.item_name ? "" : obj.item_name, item_image: obj.image, @@ -1099,6 +1099,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ get_items: function (item_code) { // To search item as per the key enter + item_code = unescape(item_code); var me = this; this.item_serial_no = {}; this.item_batch_no = {}; @@ -1164,7 +1165,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ $(this).addClass('active'); me.numeric_val = ""; me.numeric_id = "" - me.item_code = $(this).attr("data-item-code"); + me.item_code = unescape($(this).attr("data-item-code")); me.render_selected_item() me.bind_qty_event() me.update_rate() @@ -1176,33 +1177,33 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ var me = this; $(this.wrapper).on("change", ".pos-item-qty", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); + var item_code = unescape($(this).parents(".pos-selected-item-action").attr("data-item-code")); var qty = $(this).val(); me.update_qty(item_code, qty); me.update_value(); }) $(this.wrapper).on("focusout", ".pos-item-qty", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); + var item_code = unescape($(this).parents(".pos-selected-item-action").attr("data-item-code")); var qty = $(this).val(); me.update_qty(item_code, qty, true); me.update_value(); }) $(this.wrapper).find("[data-action='increase-qty']").on("click", function () { - var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); + var item_code = unescape($(this).parents(".pos-bill-item").attr("data-item-code")); var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) + 1; me.update_qty(item_code, qty); }) $(this.wrapper).find("[data-action='decrease-qty']").on("click", function () { - var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); + var item_code = unescape($(this).parents(".pos-bill-item").attr("data-item-code")); var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) - 1; me.update_qty(item_code, qty); }) $(this.wrapper).on("change", ".pos-item-disc", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); + var item_code = unescape($(this).parents(".pos-selected-item-action").attr("data-item-code")); var discount = $(this).val(); if(discount > 100){ discount = $(this).val(''); @@ -1253,7 +1254,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ update_rate: function () { var me = this; $(this.wrapper).on("change", ".pos-item-price", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); + var item_code = unescape($(this).parents(".pos-selected-item-action").attr("data-item-code")); me.set_item_details(item_code, "rate", $(this).val()); me.update_value() }) @@ -1282,9 +1283,17 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.child_doc = this.get_child_item(this.item_code); $(this.wrapper).find('.selected-item').empty(); if(this.child_doc.length) { - this.child_doc[0]["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false, - this.child_doc[0]["allow_user_to_edit_discount"] = this.pos_profile_data["allow_user_to_edit_discount"] ? true : false; - this.selected_row = $(frappe.render_template("pos_selected_item", this.child_doc[0])) + this.selected_row = $(frappe.render_template("pos_selected_item", { + idx: this.child_doc[0].idx, + item_code: escape(this.child_doc[0].item_code), + qty: this.child_doc[0].qty, + price_list_rate: this.child_doc[0].price_list_rate, + allow_user_to_edit_rate: this.pos_profile_data["allow_user_to_edit_rate"] ? true : false, + allow_user_to_edit_discount: this.pos_profile_data["allow_user_to_edit_discount"] ? true : false, + discount_percentage: this.child_doc[0].discount_percentage, + rate: this.child_doc[0].rate, + amount: this.child_doc[0].amount + })) $(this.wrapper).find('.selected-item').html(this.selected_row) } @@ -1535,7 +1544,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ $.each(this.frm.doc.items || [], function (i, d) { $(frappe.render_template("pos_bill_item_new", { - item_code: d.item_code, + item_code: escape(d.item_code), item_name: (d.item_name === d.item_code || !d.item_name) ? "" : ("
    " + d.item_name), qty: d.qty, discount_percentage: d.discount_percentage || 0.0, From 57a0fa5e8792033aa8a58920b0cdba8117944318 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 23 Jun 2020 10:45:08 +0530 Subject: [PATCH 708/929] fix: resetting lost reason in opportunity and quotation (#22378) (#22381) (cherry picked from commit 71da90034d876b8bf37be03c1332840871855ded) Co-authored-by: Anupam Kumar --- erpnext/crm/doctype/opportunity/opportunity.js | 1 + erpnext/selling/doctype/quotation/quotation.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index c9b0433fada..13079172fe8 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -95,6 +95,7 @@ frappe.ui.form.on("Opportunity", { }); } else { frm.add_custom_button(__("Reopen"), function() { + frm.set_value("lost_reasons",[]) frm.set_value("status", "Open"); frm.save(); }); diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 1571c734b7f..3c8ba467680 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -92,6 +92,8 @@ class Quotation(SellingController): self.update_lead() def on_cancel(self): + if self.lost_reasons: + self.lost_reasons = [] super(Quotation, self).on_cancel() #update enquiry status From 3c1866905776fb35dfd2c243115fb0c6fbce59fe Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 23 Jun 2020 10:49:48 +0530 Subject: [PATCH 709/929] fix: staffing Plan validation (#22379) (#22396) (cherry picked from commit 36560c8406488b0f9890d250460f7857f40c77a9) Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> --- erpnext/hr/doctype/job_offer/job_offer.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py index ef8004eedb7..cfb275b1f75 100644 --- a/erpnext/hr/doctype/job_offer/job_offer.py +++ b/erpnext/hr/doctype/job_offer/job_offer.py @@ -20,10 +20,9 @@ class JobOffer(Document): staffing_plan = get_staffing_plan_detail(self.designation, self.company, self.offer_date) check_vacancies = frappe.get_single("HR Settings").check_vacancies if staffing_plan and check_vacancies: - vacancies = frappe.db.get_value("Staffing Plan Detail", filters={"name": staffing_plan.name}, fieldname=['vacancies']) - job_offers = len(self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date)) - if vacancies - job_offers <= 0: - frappe.throw(_("There are no vacancies under staffing plan {0}").format(get_link_to_form("Staffing Plan", staffing_plan.parent))) + job_offers = self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date) + if staffing_plan.vacancies - len(job_offers) <= 0: + frappe.throw(_("There are no vacancies under staffing plan {0}").format(frappe.bold(get_link_to_form("Staffing Plan", staffing_plan.parent)))) def on_change(self): update_job_applicant(self.status, self.job_applicant) @@ -42,18 +41,22 @@ def update_job_applicant(status, job_applicant): def get_staffing_plan_detail(designation, company, offer_date): detail = frappe.db.sql(""" - SELECT spd.name as name, + SELECT DISTINCT spd.parent, sp.from_date as from_date, sp.to_date as to_date, - sp.name as parent + sp.name, + sum(spd.vacancies) as vacancies, + spd.designation FROM `tabStaffing Plan Detail` spd, `tabStaffing Plan` sp WHERE sp.docstatus=1 AND spd.designation=%s AND sp.company=%s + AND spd.parent = sp.name AND %s between sp.from_date and sp.to_date """, (designation, company, offer_date), as_dict=1) - return detail[0] if detail else None + + return frappe._dict(detail[0]) if detail else None @frappe.whitelist() def make_employee(source_name, target_doc=None): From 835b1b4f0dc52e2c813aba9464d2bc28457b7a82 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 23 Jun 2020 19:18:18 +0530 Subject: [PATCH 710/929] fix: Insert Supplier Group via List View (#22403) (#22407) (cherry picked from commit 7af7bb8311b7d3f376d3299fe3e542401c1af63d) Co-authored-by: Marica --- erpnext/setup/doctype/customer_group/customer_group.js | 5 +++-- erpnext/setup/doctype/item_group/item_group.js | 2 +- erpnext/setup/doctype/sales_person/sales_person.js | 4 ++-- erpnext/setup/doctype/supplier_group/supplier_group.js | 5 +++-- erpnext/setup/doctype/territory/territory.js | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js index c199a8e57f1..44a50191209 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.js +++ b/erpnext/setup/doctype/customer_group/customer_group.js @@ -8,7 +8,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root customer group - if(!doc.parent_customer_group) { + if(!doc.parent_customer_group && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root customer group and cannot be edited.")); } else { @@ -20,7 +20,8 @@ cur_frm.cscript.set_root_readonly = function(doc) { cur_frm.fields_dict['parent_customer_group'].get_query = function(doc,cdt,cdn) { return { filters: { - 'is_group': 1 + 'is_group': 1, + 'name': ['!=', cur_frm.doc.customer_group_name] } } } diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js index df2223192bd..9892dc3dcc0 100644 --- a/erpnext/setup/doctype/item_group/item_group.js +++ b/erpnext/setup/doctype/item_group/item_group.js @@ -66,7 +66,7 @@ frappe.ui.form.on("Item Group", { set_root_readonly: function(frm) { // read-only for root item group frm.set_intro(""); - if(!frm.doc.parent_item_group) { + if(!frm.doc.parent_item_group && !frm.doc.__islocal) { frm.set_read_only(); frm.set_intro(__("This is a root item group and cannot be edited."), true); } diff --git a/erpnext/setup/doctype/sales_person/sales_person.js b/erpnext/setup/doctype/sales_person/sales_person.js index 89ca4a9dd74..8f7593d6eef 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.js +++ b/erpnext/setup/doctype/sales_person/sales_person.js @@ -19,7 +19,7 @@ frappe.ui.form.on('Sales Person', { } } }; - + frm.make_methods = { 'Sales Order': () => frappe.new_doc("Sales Order") .then(() => frm.add_child("sales_team", {"sales_person": frm.doc.name})) @@ -33,7 +33,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root - if(!doc.parent_sales_person) { + if(!doc.parent_sales_person && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root sales person and cannot be edited.")); } else { diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js index ac5bda6e2c6..e75030d4414 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group.js @@ -8,7 +8,7 @@ cur_frm.cscript.refresh = function(doc) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root customer group - if(!doc.parent_supplier_group) { + if(!doc.parent_supplier_group && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root supplier group and cannot be edited.")); } else { @@ -20,7 +20,8 @@ cur_frm.cscript.set_root_readonly = function(doc) { cur_frm.fields_dict['parent_supplier_group'].get_query = function() { return { filters: { - 'is_group': 1 + 'is_group': 1, + 'name': ['!=', cur_frm.doc.supplier_group_name] } }; }; diff --git a/erpnext/setup/doctype/territory/territory.js b/erpnext/setup/doctype/territory/territory.js index 1eb9958ce70..ceec47ae8c6 100644 --- a/erpnext/setup/doctype/territory/territory.js +++ b/erpnext/setup/doctype/territory/territory.js @@ -20,7 +20,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root territory - if(!doc.parent_territory) { + if(!doc.parent_territory && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root territory and cannot be edited.")); } else { From fb18f78b77a4a2a436bcdac40ab515256a89796a Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 23 Jun 2020 19:19:08 +0530 Subject: [PATCH 711/929] fix: Update Packed Items via Update Items in SO (#22404) Co-authored-by: Nabin Hait --- erpnext/controllers/accounts_controller.py | 2 ++ .../product_bundle/test_product_bundle.py | 4 +-- .../doctype/sales_order/test_sales_order.py | 32 +++++++++++++------ 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3e6c7dc788f..b41ce7379a1 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -20,6 +20,7 @@ from erpnext.exceptions import InvalidCurrency from six import text_type from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions from erpnext.stock.get_item_details import get_item_warehouse +from erpnext.stock.doctype.packed_item.packed_item import make_packing_list force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules") @@ -1298,6 +1299,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.set_qty_as_per_stock_uom() parent.calculate_taxes_and_totals() if parent_doctype == "Sales Order": + make_packing_list(parent) parent.set_gross_profit() frappe.get_doc('Authorization Control').validate_approving_authority(parent.doctype, parent.company, parent.base_grand_total) diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.py b/erpnext/selling/doctype/product_bundle/test_product_bundle.py index 85a2b209f64..7d1d372b111 100644 --- a/erpnext/selling/doctype/product_bundle/test_product_bundle.py +++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.py @@ -7,7 +7,7 @@ from __future__ import unicode_literals import frappe test_records = frappe.get_test_records('Product Bundle') -def make_product_bundle(parent, items): +def make_product_bundle(parent, items, qty=None): if frappe.db.exists("Product Bundle", parent): return frappe.get_doc("Product Bundle", parent) @@ -17,7 +17,7 @@ def make_product_bundle(parent, items): }) for item in items: - product_bundle.append("items", {"item_code": item, "qty": 1}) + product_bundle.append("items", {"item_code": item, "qty": qty or 1}) product_bundle.insert() diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 6a6d7c96a22..5e360902e83 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals import frappe +import json from frappe.utils import flt, add_days, nowdate import frappe.permissions import unittest @@ -10,8 +11,9 @@ from erpnext.selling.doctype.sales_order.sales_order \ from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.selling.doctype.sales_order.sales_order import make_work_orders from erpnext.controllers.accounts_controller import update_child_qty_rate -import json from erpnext.selling.doctype.sales_order.sales_order import make_raw_material_request +from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle +from erpnext.stock.doctype.item.test_item import make_item class TestSalesOrder(unittest.TestCase): def tearDown(self): @@ -416,6 +418,26 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) frappe.set_user("Administrator") + def test_update_child_qty_rate_product_bundle(self): + # test Update Items with product bundle + if not frappe.db.exists("Item", "_Product Bundle Item"): + bundle_item = make_item("_Product Bundle Item", {"is_stock_item": 0}) + bundle_item.append("item_defaults", { + "company": "_Test Company", + "default_warehouse": "_Test Warehouse - _TC"}) + bundle_item.save(ignore_permissions=True) + + make_item("_Packed Item", {"is_stock_item": 1}) + make_product_bundle("_Product Bundle Item", ["_Packed Item"], 2) + + so = make_sales_order(item_code = "_Test Item", warehouse=None) + + added_item = json.dumps([{"item_code" : "_Product Bundle Item", "rate" : 200, 'qty' : 2}]) + update_child_qty_rate('Sales Order', added_item, so.name) + + so.reload() + self.assertEqual(so.packed_items[0].qty, 4) + def test_warehouse_user(self): frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com") frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com") @@ -456,8 +478,6 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(frappe.CancelledLinkError, dn.submit) def test_service_type_product_bundle(self): - from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle - from erpnext.stock.doctype.item.test_item import make_item make_item("_Test Service Product Bundle", {"is_stock_item": 0}) make_item("_Test Service Product Bundle Item 1", {"is_stock_item": 0}) make_item("_Test Service Product Bundle Item 2", {"is_stock_item": 0}) @@ -471,8 +491,6 @@ class TestSalesOrder(unittest.TestCase): self.assertTrue("_Test Service Product Bundle Item 2" in [d.item_code for d in so.packed_items]) def test_mix_type_product_bundle(self): - from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle - from erpnext.stock.doctype.item.test_item import make_item make_item("_Test Mix Product Bundle", {"is_stock_item": 0}) make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1}) make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0}) @@ -483,7 +501,6 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(WarehouseRequired, make_sales_order, item_code = "_Test Mix Product Bundle", warehouse="") def test_auto_insert_price(self): - from erpnext.stock.doctype.item.test_item import make_item make_item("_Test Item for Auto Price List", {"is_stock_item": 0}) frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1) @@ -518,7 +535,6 @@ class TestSalesOrder(unittest.TestCase): from erpnext.buying.doctype.purchase_order.purchase_order import update_status make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100) - from erpnext.stock.doctype.item.test_item import make_item po_item = make_item("_Test Item for Drop Shipping", {"is_stock_item": 1, "delivered_by_supplier": 1}) dn_item = make_item("_Test Regular Item", {"is_stock_item": 1}) @@ -713,7 +729,6 @@ class TestSalesOrder(unittest.TestCase): def test_serial_no_based_delivery(self): frappe.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1) - from erpnext.stock.doctype.item.test_item import make_item item = make_item("_Reserved_Serialized_Item", {"is_stock_item": 1, "maintain_stock": 1, "has_serial_no": 1, @@ -835,7 +850,6 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(frappe.LinkExistsError, so_doc.cancel) def test_request_for_raw_materials(self): - from erpnext.stock.doctype.item.test_item import make_item item = make_item("_Test Finished Item", {"is_stock_item": 1, "maintain_stock": 1, "valuation_rate": 500, From 0a6b5bc83f07353ec360e425e0aff63e3446817c Mon Sep 17 00:00:00 2001 From: Revant Nandgaonkar Date: Tue, 23 Jun 2020 20:11:35 +0530 Subject: [PATCH 712/929] ci: github release action for v12 --- .github/workflows/docker-release.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/docker-release.yml diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml new file mode 100644 index 00000000000..4b1147e79f9 --- /dev/null +++ b/.github/workflows/docker-release.yml @@ -0,0 +1,14 @@ +name: Trigger Docker build on release +on: + release: + types: [released] +jobs: + curl: + runs-on: ubuntu-latest + container: + image: alpine:latest + steps: + - name: curl + run: | + apk add curl bash + curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests From bf81b42219e10ecb685aaeb5beaab7fc8922ddcd Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Tue, 23 Jun 2020 16:55:35 +0530 Subject: [PATCH 713/929] fix: add error prompt for wrong date range (cherry picked from commit f5b7bd9dcbe389cd66373ff8feabd333196ecba7) --- .../report/item_wise_sales_history/item_wise_sales_history.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index 08a98ba6c07..f1b8bc34efb 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -9,6 +9,9 @@ from frappe.utils.nestedset import get_descendants_of def execute(filters=None): filters = frappe._dict(filters or {}) + if filters.from_date > filters.to_date: + frappe.throw(_('From Date cannot be greater than To Date')) + columns = get_columns(filters) data = get_data(filters) return columns, data From c28adea2d0a557f249d888b82ce38b0917925976 Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Thu, 25 Jun 2020 08:24:01 +0530 Subject: [PATCH 714/929] fix: change error messages in RFQ Before: 1. Row {0}: For supplier {0} Email Address is required to send email 2. Email sent to supplier 3. Request for Quotation is disabled to access from portal, for more check portal settings. 4. Supplier Quotation created. After: 1. Row {0}: For Supplier {0} Email Address is Required to Send Email 2. Email sent to Supplier 3. The Access to Request for Quotation From Portal is Disabled. To Allow Access, Enable it in Portal Settings. 4. Supplier Quotation Created. (cherry picked from commit da0385cd3982abe986018d62ff926abb4341f242) --- .../request_for_quotation/request_for_quotation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index dfdb487f9e0..56af4d90279 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -51,7 +51,7 @@ class RequestforQuotation(BuyingController): def validate_email_id(self, args): if not args.email_id: - frappe.throw(_("Row {0}: For supplier {0} Email Address is required to send email").format(args.idx, args.supplier)) + frappe.throw(_("Row {0}: For Supplier {0}, Email Address is Required to Send Email").format(args.idx, args.supplier)) def on_submit(self): frappe.db.set(self, 'status', 'Submitted') @@ -154,7 +154,7 @@ class RequestforQuotation(BuyingController): sender=sender,attachments = attachments, send_email=True, doctype=self.doctype, name=self.name)["name"] - frappe.msgprint(_("Email sent to supplier {0}").format(data.supplier)) + frappe.msgprint(_("Email Sent to Supplier {0}").format(data.supplier)) def get_attachments(self): attachments = [d.name for d in get_attachments(self.doctype, self.name)] @@ -193,7 +193,7 @@ def send_supplier_emails(rfq_name): def check_portal_enabled(reference_doctype): if not frappe.db.get_value('Portal Menu Item', {'reference_doctype': reference_doctype}, 'enabled'): - frappe.throw(_("Request for Quotation is disabled to access from portal, for more check portal settings.")) + frappe.throw(_("The Access to Request for Quotation From Portal is Disabled. To Allow Access, Enable it in Portal Settings.")) def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context @@ -259,7 +259,7 @@ def create_supplier_quotation(doc): sq_doc.flags.ignore_permissions = True sq_doc.run_method("set_missing_values") sq_doc.save() - frappe.msgprint(_("Supplier Quotation {0} created").format(sq_doc.name)) + frappe.msgprint(_("Supplier Quotation {0} Created").format(sq_doc.name)) return sq_doc.name except Exception: return None From 99a9f98688c0dbe8def1a890b93f4ce69cdc7518 Mon Sep 17 00:00:00 2001 From: Sun Howwrongbum Date: Thu, 25 Jun 2020 10:05:00 +0530 Subject: [PATCH 715/929] fix: UnboundLocalError when setting product_info --- erpnext/portal/product_configurator/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 69ff1dcccaa..60aa3b64e82 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -261,13 +261,12 @@ def get_next_attribute_and_values(item_code, selected_attributes): if exact_match: data = get_product_info_for_website(exact_match[0]) product_info = data.product_info + product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock) if not data.cart_settings.show_price: product_info = None else: product_info = None - product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock) - return { 'next_attribute': next_attribute, 'valid_options_for_attributes': valid_options_for_attributes, From d9e7b480eb51e2826dcff253fac35f0dac5a063e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 25 Jun 2020 17:11:36 +0530 Subject: [PATCH 716/929] fix: cannot cancel assets with repair pending (#22440) (#22453) * fix: cannot cancel assets with repair pending * fix: message * Update asset.py Co-authored-by: Nabin Hait (cherry picked from commit 40847aeac3c5a138dc422d4996979f8a04c0e74a) Co-authored-by: Saqib --- erpnext/assets/doctype/asset/asset.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index ad7edd1ead2..bfb44e5c30a 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -407,6 +407,8 @@ class Asset(AccountsController): row.expected_value_after_useful_life = asset_value_after_full_schedule def validate_cancellation(self): + if self.status in ("In Maintenance", "Out of Order"): + frappe.throw(_("There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset.")) if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"): frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status)) From 3cb8173034d618f9d560992365a9a891a4263617 Mon Sep 17 00:00:00 2001 From: John Clarke Date: Thu, 25 Jun 2020 07:30:10 -0600 Subject: [PATCH 717/929] fix: stock_ageing.py report convert string numeric to flt value This PR combines these two PRs below applied to develop, to be applied to version-12-hotfix (The mergifyio process failed for some reason.) https://github.com/frappe/erpnext/pull/22430 https://github.com/frappe/erpnext/pull/22206 --- erpnext/stock/report/stock_ageing/stock_ageing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 803a5c81a3b..99d816c4a24 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -178,14 +178,14 @@ def get_fifo_queue(filters, sle=None): qty_to_pop = abs(d.actual_qty) while qty_to_pop: batch = fifo_queue[0] if fifo_queue else [0, None] - if 0 < batch[0] <= qty_to_pop: + if 0 < flt(batch[0]) <= qty_to_pop: # if batch qty > 0 # not enough or exactly same qty in current batch, clear batch - qty_to_pop -= batch[0] + qty_to_pop -= flt(batch[0]) transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0)) else: # all from current batch - batch[0] -= qty_to_pop + batch[0] = flt(batch[0]) - qty_to_pop transferred_item_details[(d.voucher_no, d.name)].append([qty_to_pop, batch[1]]) qty_to_pop = 0 From 6089023df7c42329cda796e40980a4f0d0e64cee Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 26 Jun 2020 09:23:36 +0530 Subject: [PATCH 718/929] fix: grammatical fix on error message (#22464) Before: Fiscal Year: 2020 does not exists After: Fiscal Year 2020 Does Not Exist (cherry picked from commit 978722621fb804abc31d791c4176a90dc024cdac) Co-authored-by: Michelle Alva <50285544+michellealva@users.noreply.github.com> --- erpnext/controllers/trends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/trends.py b/erpnext/controllers/trends.py index 092baa4018f..9b4b0eb9173 100644 --- a/erpnext/controllers/trends.py +++ b/erpnext/controllers/trends.py @@ -33,7 +33,7 @@ def validate_filters(filters): frappe.throw(_("{0} is mandatory").format(f)) if not frappe.db.exists("Fiscal Year", filters.get("fiscal_year")): - frappe.throw(_("Fiscal Year: {0} does not exists").format(filters.get("fiscal_year"))) + frappe.throw(_("Fiscal Year {0} Does Not Exist").format(filters.get("fiscal_year"))) if filters.get("based_on") == filters.get("group_by"): frappe.throw(_("'Based On' and 'Group By' can not be same")) From 6bd8c048b6f80c18bf4837f188842f215f1c8dee Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 25 Jun 2020 21:30:45 +0530 Subject: [PATCH 719/929] fix: Handling Empty tables in Production Plan --- .../production_plan/production_plan.py | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 61b4ca838b3..32ecda2a841 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -98,11 +98,17 @@ class ProductionPlan(Document): elif self.get_items_from == "Material Request": self.get_mr_items() + def get_so_mr_list(self, field, table): + """Returns a list of Sales Orders or Material Requests from the respective tables""" + so_mr_list = [d.get(field) for d in self.get(table) if d.get(field)] + return so_mr_list + def get_so_items(self): - so_list = [d.sales_order for d in self.sales_orders if d.sales_order] - if not so_list: - msgprint(_("Please enter Sales Orders in the above table")) - return [] + # Check for empty table or empty rows + if not self.get("sales_orders") or not self.get_so_mr_list("sales_order", "sales_orders"): + frappe.throw(_("Please fill the Sales Orders table"), title=_("Sales Orders Required")) + + so_list = self.get_so_mr_list("sales_order", "sales_orders") item_condition = "" if self.item_code: @@ -134,10 +140,11 @@ class ProductionPlan(Document): self.calculate_total_planned_qty() def get_mr_items(self): - mr_list = [d.material_request for d in self.material_requests if d.material_request] - if not mr_list: - msgprint(_("Please enter Material Requests in the above table")) - return [] + # Check for empty table or empty rows + if not self.get("material_requests") or not self.get_so_mr_list("material_request", "material_requests"): + frappe.throw(_("Please fill the Material Requests table"), title=_("Material Requests Required")) + + mr_list = self.get_so_mr_list("material_request", "material_requests") item_condition = "" if self.item_code: @@ -614,7 +621,13 @@ def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None): doc = frappe._dict(json.loads(doc)) doc['mr_items'] = [] - po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items') + + po_items = doc.get('po_items') + # Check for empty table or empty rows + if not po_items or not [row.get('item_code') for row in po_items if row.get('item_code')]: + frappe.throw(_("Items to Manufacture are required to pull the Raw Materials associated with it."), + title=_("Items Required")) + company = doc.get('company') warehouse = doc.get('for_warehouse') From cfc47bc011d50437bd6d6c08889bdc05d446ce55 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 26 Jun 2020 14:06:04 +0530 Subject: [PATCH 720/929] fix: Sales Order to MR test failing - Items to Manufacture can come as doc.items from Sales Order --- .../manufacturing/doctype/production_plan/production_plan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 32ecda2a841..8ff691cc300 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -622,7 +622,7 @@ def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None): doc['mr_items'] = [] - po_items = doc.get('po_items') + po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items') # Check for empty table or empty rows if not po_items or not [row.get('item_code') for row in po_items if row.get('item_code')]: frappe.throw(_("Items to Manufacture are required to pull the Raw Materials associated with it."), From e73dec158b41ec2097893af81289aa833603e1c3 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Fri, 26 Jun 2020 12:46:40 +0530 Subject: [PATCH 721/929] enable show_configure_button when shopping cart is enabled (cherry picked from commit 27d27c924dde64d846f2200b7f4570e819058d7c) --- .../shopping_cart_settings/shopping_cart_settings.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js index e1510f53354..45889872dcf 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js @@ -13,6 +13,12 @@ $.extend(cur_frm.cscript, { }, enable_checkout: function(){ toggle_mandatory(cur_frm) + }, + enabled: function() { + if(cur_frm.doc.enabled == 1) { + cur_frm.doc.show_configure_button = 1; + cur_frm.refresh_field('show_configure_button'); + } } }); From 8a542bc9df69517f51db37232234a0ef3df57f4d Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 26 Jun 2020 15:07:32 +0530 Subject: [PATCH 722/929] Update shopping_cart_settings.js (cherry picked from commit 13e8c3217247dca8d17e1330b512d35a9a94d956) --- .../doctype/shopping_cart_settings/shopping_cart_settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js index 45889872dcf..ffc5daba62f 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js @@ -15,7 +15,7 @@ $.extend(cur_frm.cscript, { toggle_mandatory(cur_frm) }, enabled: function() { - if(cur_frm.doc.enabled == 1) { + if (cur_frm.doc.enabled === 1) { cur_frm.doc.show_configure_button = 1; cur_frm.refresh_field('show_configure_button'); } From 0e7ab9526cb36ae8abfe7f7e092178d2f3624946 Mon Sep 17 00:00:00 2001 From: britlog Date: Tue, 9 Jun 2020 22:50:49 +0200 Subject: [PATCH 723/929] fix: product_info --- erpnext/shopping_cart/product_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index 7c08f5b5b24..29617a87485 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -55,7 +55,7 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): def set_product_info_for_website(item): """set product price uom for website""" - product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True) + product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get("product_info") if product_info: item.update(product_info) From 584a8f52a5ac22466660266368f0ac3d6e742060 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 25 Jun 2020 16:02:18 +0530 Subject: [PATCH 724/929] fix: Disable Renaming in Serial No (cherry picked from commit f311f52f6ecd61a34e640a246abd4a33711c988a) --- erpnext/stock/doctype/serial_no/serial_no.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index d9f8b627545..2be14c8006a 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.json +++ b/erpnext/stock/doctype/serial_no/serial_no.json @@ -1,7 +1,6 @@ { "actions": [], "allow_import": 1, - "allow_rename": 1, "autoname": "field:serial_no", "creation": "2013-05-16 10:59:15", "description": "Distinct unit of an Item", @@ -427,7 +426,7 @@ "icon": "fa fa-barcode", "idx": 1, "links": [], - "modified": "2020-05-21 19:29:58.517772", + "modified": "2020-06-25 15:53:50.900855", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", From ad0aca2fc6b33c1e7eff79317a2052bc0d787a7f Mon Sep 17 00:00:00 2001 From: michellealva Date: Fri, 26 Jun 2020 19:56:00 +0530 Subject: [PATCH 725/929] fix: apply filters in all transactions (cherry picked from commit 08f842d12ee2472fc7a33472b4f888f73d331bb8) --- erpnext/public/js/controllers/transaction.js | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index e6e8ae7cb31..21968010d71 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -158,6 +158,27 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }; }); } + if (this.frm.fields_dict["items"].grid.get_field("cost_center")) { + this.frm.set_query("cost_center", "items", function(doc) { + return { + filters: { + "company": doc.company, + "is_group": 0 + } + }; + }); + } + + if (this.frm.fields_dict["items"].grid.get_field("expense_account")) { + this.frm.set_query("expense_account", "items", function(doc) { + return { + filters: { + "company": doc.company + } + }; + }); + } + if(frappe.meta.get_docfield(this.frm.doc.doctype, "pricing_rules")) { this.frm.set_indicator_formatter('pricing_rule', function(doc) { From 104b3179290d0531d6c79144dfbf3be16a4ff97b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 28 Jun 2020 11:12:20 +0530 Subject: [PATCH 726/929] fix: indentation (cherry picked from commit d5736711ce04131364b079a29b10f6ee585d3dd7) --- erpnext/public/js/controllers/transaction.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 21968010d71..2c4277674cf 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -160,7 +160,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } if (this.frm.fields_dict["items"].grid.get_field("cost_center")) { this.frm.set_query("cost_center", "items", function(doc) { - return { + return { filters: { "company": doc.company, "is_group": 0 @@ -169,9 +169,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }); } - if (this.frm.fields_dict["items"].grid.get_field("expense_account")) { + if (this.frm.fields_dict["items"].grid.get_field("expense_account")) { this.frm.set_query("expense_account", "items", function(doc) { - return { + return { filters: { "company": doc.company } @@ -179,7 +179,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }); } - if(frappe.meta.get_docfield(this.frm.doc.doctype, "pricing_rules")) { this.frm.set_indicator_formatter('pricing_rule', function(doc) { return (doc.rule_applied) ? "green" : "red"; From 6aa7c404f81d59a23e92f556c616402753cbc830 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 28 Jun 2020 17:08:12 +0530 Subject: [PATCH 727/929] fix: letter head not found in opening invoice creation tool (cherry picked from commit 04cf2e029f98874b4eb8043ccbdce08f649f36d6) --- .../opening_invoice_creation_tool.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index 54464e71c4e..a53417eedf9 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -68,6 +68,9 @@ class OpeningInvoiceCreationTool(Document): if not self.company: frappe.throw(_("Please select the Company")) + company_details = frappe.get_cached_value('Company', self.company, + ["default_currency", "default_letter_head"], as_dict=1) or {} + for row in self.invoices: if not row.qty: row.qty = 1.0 @@ -99,6 +102,12 @@ class OpeningInvoiceCreationTool(Document): if not args: continue + if company_details: + args.update({ + "currency": company_details.get("default_currency"), + "letter_head": company_details.get("default_letter_head") + }) + doc = frappe.get_doc(args).insert() doc.submit() names.append(doc.name) @@ -172,8 +181,7 @@ class OpeningInvoiceCreationTool(Document): "due_date": row.due_date, "posting_date": row.posting_date, frappe.scrub(party_type): row.party, - "doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice", - "currency": frappe.get_cached_value('Company', self.company, "default_currency") + "doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice" }) accounting_dimension = get_accounting_dimensions() From a20957d3555e57c21b4d107f631bd878a1ca7d3d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2020 17:36:43 +0530 Subject: [PATCH 728/929] fix: handle nonetype issue for packed items (#22493) (#22494) (cherry picked from commit 73edba0e10d0a33fdd4186a83673e9d2295d578f) Co-authored-by: Mangesh-Khairnar --- .../pending_so_items_for_purchase_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py index 14d80315823..e89c45182fd 100644 --- a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py +++ b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py @@ -158,7 +158,7 @@ def get_data(): } pending_so.append(so_record) else: - for item in bundled_item_map.get((so.name, so.item_code)): + for item in bundled_item_map.get((so.name, so.item_code), []): material_requests_against_so = materials_request_dict.get((so.name, item.item_code)) or {} if flt(item.qty) > flt(material_requests_against_so.get('qty')): so_record = { From 5d5cb0ac28404ba8a97490661d43d934c7fd4794 Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 30 Jun 2020 08:21:07 +0530 Subject: [PATCH 729/929] fix: Set Root as Parent if no parent in new tree view node (#22507) --- erpnext/setup/doctype/customer_group/customer_group.py | 5 ++++- erpnext/setup/doctype/sales_person/sales_person.py | 5 ++++- erpnext/setup/doctype/territory/territory.py | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py index f62613ea1bb..68e1ccb6356 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.py +++ b/erpnext/setup/doctype/customer_group/customer_group.py @@ -6,9 +6,12 @@ import frappe from frappe import _ -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_root_of class CustomerGroup(NestedSet): nsm_parent_field = 'parent_customer_group' + def validate(self): + if not self.parent_customer_group: + self.parent_customer_group = get_root_of("Customer Group") def on_update(self): self.validate_name_with_customer() diff --git a/erpnext/setup/doctype/sales_person/sales_person.py b/erpnext/setup/doctype/sales_person/sales_person.py index 3379534cf82..19c2e5b9543 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.py +++ b/erpnext/setup/doctype/sales_person/sales_person.py @@ -5,13 +5,16 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_root_of from erpnext import get_default_currency class SalesPerson(NestedSet): nsm_parent_field = 'parent_sales_person' def validate(self): + if not self.parent_sales_person: + self.parent_sales_person = get_root_of("Sales Person") + for d in self.get('targets') or []: if not flt(d.target_qty) and not flt(d.target_amount): frappe.throw(_("Either target qty or target amount is mandatory.")) diff --git a/erpnext/setup/doctype/territory/territory.py b/erpnext/setup/doctype/territory/territory.py index 095bd1c179c..89423b5a693 100644 --- a/erpnext/setup/doctype/territory/territory.py +++ b/erpnext/setup/doctype/territory/territory.py @@ -8,12 +8,15 @@ import frappe from frappe.utils import flt from frappe import _ -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_root_of class Territory(NestedSet): nsm_parent_field = 'parent_territory' def validate(self): + if not self.parent_territory: + self.parent_territory = get_root_of("Territory") + for d in self.get('targets') or []: if not flt(d.target_qty) and not flt(d.target_amount): frappe.throw(_("Either target qty or target amount is mandatory")) From 4e17c405899ee5db13f047ec179f1e48e392b26d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 30 Jun 2020 18:12:47 +0530 Subject: [PATCH 730/929] fix: Refactor dashboard links in leave policy (bp #22519) (#22529) * fix: Refactor dashboard links in leave policy (#22519) * fix: refactor dashboard links in leave policy * fx: code fix * fix: add labels to links * fix: code change (cherry picked from commit ec84afa86d54b30c6946ef6bd2e71189ca9bc04b) * fix: remove extra lines Co-authored-by: Michelle Alva <50285544+michellealva@users.noreply.github.com> Co-authored-by: Rucha Mahabal --- .../hr/doctype/leave_policy/leave_policy_dashboard.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py index 48a204596c3..71819e76ed8 100644 --- a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py +++ b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py @@ -1,4 +1,5 @@ from __future__ import unicode_literals +from frappe import _ def get_data(): return { @@ -8,13 +9,12 @@ def get_data(): }, 'transactions': [ { - 'items': ['Employee'] - }, - { - 'items': ['Employee Grade'] + 'label': _('Employees'), + 'items': ['Employee', 'Employee Grade'] }, { + 'label': _('Leaves'), 'items': ['Leave Allocation'] }, ] - } \ No newline at end of file + } From c8d6e8f19d516c120fd7573101e9987d370d2ded Mon Sep 17 00:00:00 2001 From: Afshan Date: Mon, 29 Jun 2020 21:39:35 +0530 Subject: [PATCH 731/929] feat: Autofill Supplier pop-up when only 1 Supplier in RFQ (cherry picked from commit 06f11aba0bb79689dda2d30be78db9a2e5ae8e58) --- .../doctype/request_for_quotation/request_for_quotation.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 455bd68ecff..4a937f7f0d3 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -166,7 +166,8 @@ frappe.ui.form.on("Request for Quotation",{ { "fieldtype": "Select", "label": __("Supplier"), "fieldname": "supplier", "options": doc.suppliers.map(d => d.supplier), - "reqd": 1 }, + "reqd": 1, + "default": doc.suppliers.length === 1 ? doc.suppliers[0].supplier_name : "" }, { "fieldtype": "Button", "label": __('Create Supplier Quotation'), "fieldname": "make_supplier_quotation", "cssClass": "btn-primary" }, ] From bf8a26c31e8063aa34c98ee6602e44c013d50197 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 29 Jun 2020 17:35:27 +0530 Subject: [PATCH 732/929] fix: take parent cost center for child if no cost center at child (#22496) (cherry picked from commit ba5f571b0d243a8d44f1d189ec42db187ca91d9d) --- erpnext/hr/doctype/expense_claim/expense_claim.js | 8 +++++--- erpnext/hr/doctype/expense_claim/expense_claim.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index eeb56db804a..45351d858b9 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -299,6 +299,11 @@ frappe.ui.form.on("Expense Claim", { cost_center: function(frm) { frm.events.set_child_cost_center(frm); }, + + validate: function(frm) { + frm.events.set_child_cost_center(frm); + }, + set_child_cost_center: function(frm){ (frm.doc.expenses || []).forEach(function(d) { if (!d.cost_center){ @@ -348,9 +353,6 @@ frappe.ui.form.on("Expense Claim", { }); frappe.ui.form.on("Expense Claim Detail", { - expenses_add: function(frm, cdt, cdn) { - frm.events.set_child_cost_center(frm); - }, amount: function(frm, cdt, cdn) { var child = locals[cdt][cdn]; frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.amount); diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index 4d97b0d0c72..e4fdaccf3be 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -128,7 +128,7 @@ class ExpenseClaim(AccountsController): "debit": data.sanctioned_amount, "debit_in_account_currency": data.sanctioned_amount, "against": self.employee, - "cost_center": data.cost_center + "cost_center": data.cost_center or self.cost_center }, item=data) ) From bae83679c06732f30abb316619f7eeedc728bcb3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 19 Jun 2020 17:28:58 +0530 Subject: [PATCH 733/929] Revert "fix: update remark on submitting payment entry" (cherry picked from commit 7d035fac363be3a6b1d28c13a767cdbb227e5a8f) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index a2e1a4a1612..1c5bea6cf15 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -452,6 +452,8 @@ class PaymentEntry(AccountsController): frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction")) def set_remarks(self): + if self.remarks: return + if self.payment_type=="Internal Transfer": remarks = [_("Amount {0} {1} transferred from {2} to {3}") .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)] From 30e33bef9ec8bfecb9cae76b981500afa120300d Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Thu, 2 Jul 2020 13:26:22 +0550 Subject: [PATCH 734/929] bumped to version 12.10.1 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a46d4ad58ff..457a8358b20 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.10.0' +__version__ = '12.10.1' def get_default_company(user=None): '''Get default company for user''' From 3c0f38fa0293da88be217585cb9f2f90ae10f910 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Thu, 2 Jul 2020 14:52:50 +0530 Subject: [PATCH 735/929] fix: made "Subscription Section", "Auto Repeat" and Hub Publishing" collapsible by default (#22535) --- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 5 ++--- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 5 ++--- erpnext/buying/doctype/purchase_order/purchase_order.json | 5 ++--- erpnext/selling/doctype/sales_order/sales_order.json | 5 ++--- erpnext/stock/doctype/item/item.json | 5 ++--- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index a1a20de0503..f65479ca01d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1,5 +1,4 @@ { - "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", @@ -1233,6 +1232,7 @@ "print_hide": 1 }, { + "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", "label": "Subscription Section", @@ -1298,8 +1298,7 @@ "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, - "links": [], - "modified": "2020-04-18 13:05:25.199832", + "modified": "2020-07-01 12:41:54.851217", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 6e0a30d48e0..205d535e188 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1,5 +1,4 @@ { - "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-24 19:29:05", @@ -1494,6 +1493,7 @@ "print_hide": 1 }, { + "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", "label": "Subscription Section" @@ -1569,8 +1569,7 @@ "icon": "fa fa-file-text", "idx": 181, "is_submittable": 1, - "links": [], - "modified": "2020-05-19 17:00:57.208696", + "modified": "2020-07-01 12:41:29.484813", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 2018d068edf..5dacfb0e02e 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1,5 +1,4 @@ { - "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", @@ -998,6 +997,7 @@ "print_hide": 1 }, { + "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", "label": "Subscription Section" @@ -1055,8 +1055,7 @@ "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, - "links": [], - "modified": "2020-06-12 14:08:11.777120", + "modified": "2020-07-01 12:40:45.240948", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index b03b6a6fa7e..c6a3205f1e7 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -1,5 +1,4 @@ { - "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-06-18 12:39:59", @@ -1140,6 +1139,7 @@ }, { "allow_on_submit": 1, + "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", "label": "Auto Repeat Section", @@ -1196,8 +1196,7 @@ "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, - "links": [], - "modified": "2020-05-19 21:36:57.437325", + "modified": "2020-07-01 12:39:57.698621", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 7d2e3112fb3..b7c2b962f97 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -1,5 +1,4 @@ { - "actions": [], "allow_guest_to_view": 1, "allow_import": 1, "allow_rename": 1, @@ -987,6 +986,7 @@ "read_only": 1 }, { + "collapsible": 1, "depends_on": "eval:(!doc.is_item_from_hub)", "fieldname": "hub_publishing_sb", "fieldtype": "Section Break", @@ -1058,9 +1058,8 @@ "icon": "fa fa-tag", "idx": 2, "image_field": "image", - "links": [], "max_attachments": 1, - "modified": "2020-04-07 15:56:06.195722", + "modified": "2020-07-01 12:43:10.656530", "modified_by": "Administrator", "module": "Stock", "name": "Item", From 336ca33a9898f24b194ff1671b523c3f982d397a Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 2 Jul 2020 14:58:20 +0530 Subject: [PATCH 736/929] fix: Set label if domains is set (#22523) --- erpnext/templates/includes/footer/footer_powered.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html index 4274ba12cf5..f0f65a37618 100644 --- a/erpnext/templates/includes/footer/footer_powered.html +++ b/erpnext/templates/includes/footer/footer_powered.html @@ -12,8 +12,8 @@ } %} {% set link = '' %} -{% set label = domains[0].domain %} {% if domains %} + {% set label = domains[0].domain %} {% set link = links[label] %} {% endif %} From 1b18bf03885a77d6ccfe04f44b1ae504803dbf9b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 3 Jul 2020 21:23:23 +0530 Subject: [PATCH 737/929] fix: Update item tax only if item code available --- erpnext/regional/united_arab_emirates/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index 772bbf5914b..a0425f6b1c2 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -11,14 +11,17 @@ def update_itemised_tax_data(doc): for row in doc.items: tax_rate = 0.0 - item_tax_rate = frappe.parse_json(row.item_tax_rate) + item_tax_rate = 0.0 + + if row.item_tax_rate: + item_tax_rate = frappe.parse_json(row.item_tax_rate) # First check if tax rate is present # If not then look up in item_wise_tax_detail if item_tax_rate: for account, rate in iteritems(item_tax_rate): tax_rate += rate - elif itemised_tax.get(row.item_code): + elif row.item_code and itemised_tax.get(row.item_code): tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()]) row.tax_rate = flt(tax_rate, row.precision("tax_rate")) From 735d7f732d324da267ac5e6eda572a2b758989bf Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 3 Jul 2020 22:59:00 +0530 Subject: [PATCH 738/929] fix: Consider company fiscal for getting balalnce (cherry picked from commit 96d40ec9dacc992fd45b9bfbb2007cec0b682084) --- erpnext/accounts/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index e093e1d668c..5647c6f9dca 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -113,7 +113,7 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company acc = frappe.get_doc("Account", account) try: - year_start_date = get_fiscal_year(date, verbose=0)[1] + year_start_date = get_fiscal_year(date, company=company, verbose=0)[1] except FiscalYearError: if getdate(date) > getdate(nowdate()): # if fiscal year not found and the date is greater than today @@ -767,10 +767,10 @@ def get_children(doctype, parent, company, is_root=False): company_currency = frappe.get_cached_value('Company', company, "default_currency") for each in acc: each["company_currency"] = company_currency - each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False)) + each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False, company=company)) if each.account_currency != company_currency: - each["balance_in_account_currency"] = flt(get_balance_on(each.get("value"))) + each["balance_in_account_currency"] = flt(get_balance_on(each.get("value"), company=company)) return acc From b9e4df557b71840bcf76a15dbb4c974058a21791 Mon Sep 17 00:00:00 2001 From: bhavesh95863 <34086262+bhavesh95863@users.noreply.github.com> Date: Mon, 6 Jul 2020 18:29:37 +0530 Subject: [PATCH 739/929] fix: Message field mandtory even after unchecking collect progress checkbox (#22594) Message field mandtory even after unchecking collect progress checkbox --- erpnext/projects/doctype/project/project.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 9ba9ccfb4e8..cafa1bbb911 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -119,9 +119,7 @@ frappe.ui.form.on("Project", { }, collect_progress: function(frm) { - if (frm.doc.collect_progress) { - frm.set_df_property("message", "reqd", 1); - } + frm.set_df_property("message", "reqd", frm.doc.collect_progress); } }); @@ -140,4 +138,4 @@ function open_form(frm, doctype, child_doctype, parentfield) { frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); }); -} \ No newline at end of file +} From e0a9ae612ea81b19fcf559e2bf350b4cc6a8bb20 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 7 Jul 2020 14:14:07 +0530 Subject: [PATCH 740/929] fix: Payment reco error in multicompany setup (cherry picked from commit e2fdff57776e6627a6cc66565c6ff6440f2def6f) --- .../payment_reconciliation.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 30804961861..8dc28e878cb 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -101,10 +101,10 @@ class PaymentReconciliation(Document): Having amount > 0 """.format( - doc=voucher_type, - dr_or_cr=dr_or_cr, - reconciled_dr_or_cr=reconciled_dr_or_cr, - party_type_field=frappe.scrub(self.party_type)), + doc=voucher_type, + dr_or_cr=dr_or_cr, + reconciled_dr_or_cr=reconciled_dr_or_cr, + party_type_field=frappe.scrub(self.party_type)), { 'party': self.party, 'party_type': self.party_type, @@ -170,7 +170,7 @@ class PaymentReconciliation(Document): reconcile_against_document(lst) if dr_or_cr_notes: - reconcile_dr_cr_note(dr_or_cr_notes) + reconcile_dr_cr_note(dr_or_cr_notes, self.receivable_payable_account) msgprint(_("Successfully Reconciled")) self.get_unreconciled_entries() @@ -261,7 +261,8 @@ class PaymentReconciliation(Document): return cond -def reconcile_dr_cr_note(dr_cr_notes): +def reconcile_dr_cr_note(dr_cr_notes, receivable_payable_account): + company = frappe.db.get_value('Account', receivable_payable_account, 'company') for d in dr_cr_notes: voucher_type = ('Credit Note' if d.voucher_type == 'Sales Invoice' else 'Debit Note') @@ -273,6 +274,7 @@ def reconcile_dr_cr_note(dr_cr_notes): "doctype": "Journal Entry", "voucher_type": voucher_type, "posting_date": today(), + "company": company, "accounts": [ { 'account': d.account, From 402b273d9bf065c051e18f4748178aaa9b40d622 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 7 Jul 2020 14:37:17 +0530 Subject: [PATCH 741/929] fix: Pass company (cherry picked from commit ca351e354915042c008d8a9e10464b76eeb6ff15) --- .../doctype/payment_reconciliation/payment_reconciliation.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 8dc28e878cb..8eaad7acd4b 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -170,7 +170,7 @@ class PaymentReconciliation(Document): reconcile_against_document(lst) if dr_or_cr_notes: - reconcile_dr_cr_note(dr_or_cr_notes, self.receivable_payable_account) + reconcile_dr_cr_note(dr_or_cr_notes, self.company) msgprint(_("Successfully Reconciled")) self.get_unreconciled_entries() @@ -261,8 +261,7 @@ class PaymentReconciliation(Document): return cond -def reconcile_dr_cr_note(dr_cr_notes, receivable_payable_account): - company = frappe.db.get_value('Account', receivable_payable_account, 'company') +def reconcile_dr_cr_note(dr_cr_notes, company): for d in dr_cr_notes: voucher_type = ('Credit Note' if d.voucher_type == 'Sales Invoice' else 'Debit Note') From 321aba642d03f4c5ebad1f97211cede52da30047 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Mon, 6 Jul 2020 23:54:07 +0530 Subject: [PATCH 742/929] shopping cart issue fixes --- erpnext/public/js/shopping_cart.js | 12 + .../shopping_cart_settings.js | 35 +- .../shopping_cart_settings.json | 930 ++++-------------- .../shopping_cart_settings.py | 1 + .../generators/item/item_configure.html | 2 +- 5 files changed, 213 insertions(+), 767 deletions(-) diff --git a/erpnext/public/js/shopping_cart.js b/erpnext/public/js/shopping_cart.js index 44a8cd0067b..8d2932992e7 100644 --- a/erpnext/public/js/shopping_cart.js +++ b/erpnext/public/js/shopping_cart.js @@ -51,10 +51,12 @@ frappe.ready(function() { if (referral_sales_partner) { $(".txtreferral_sales_partner").val(referral_sales_partner); } + // update login shopping_cart.show_shoppingcart_dropdown(); shopping_cart.set_cart_count(); shopping_cart.bind_dropdown_cart_buttons(); + shopping_cart.show_cart_navbar(); }); $.extend(shopping_cart, { @@ -177,4 +179,14 @@ $.extend(shopping_cart, { }, + show_cart_navbar: function () { + frappe.call({ + method: "erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings.is_cart_enabled", + callback: function(r) { + if (r.message == 0) { + $(".shopping-cart").toggleClass('hidden', true); + } + } + }); + } }); diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js index ffc5daba62f..23814f2aabf 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js @@ -1,31 +1,32 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -$.extend(cur_frm.cscript, { - onload: function() { - if(cur_frm.doc.__onload && cur_frm.doc.__onload.quotation_series) { - cur_frm.fields_dict.quotation_series.df.options = cur_frm.doc.__onload.quotation_series; - cur_frm.refresh_field("quotation_series"); +frappe.ui.form.on("Shopping Cart Settings", { + onload: function(frm) { + if(frm.doc.__onload && frm.doc.__onload.quotation_series) { + frm.fields_dict.quotation_series.df.options = frm.doc.__onload.quotation_series; + frm.refresh_field("quotation_series"); } }, - refresh: function(){ - toggle_mandatory(cur_frm) + refresh: function(frm){ + toggle_mandatory(frm) }, - enable_checkout: function(){ - toggle_mandatory(cur_frm) + enable_checkout: function(frm){ + toggle_mandatory(frm) }, - enabled: function() { - if (cur_frm.doc.enabled === 1) { - cur_frm.doc.show_configure_button = 1; - cur_frm.refresh_field('show_configure_button'); + enabled: function(frm) { + if (frm.doc.enabled === 1) { + frm.set_value('enable_variants', 1); } + let is_required = frm.doc.enabled ? 1 : 0; + frm.toggle_reqd(["company", "default_customer_group", "quotation_series"], is_required); } }); -function toggle_mandatory (cur_frm){ - cur_frm.toggle_reqd("payment_gateway_account", false); - if(cur_frm.doc.enabled && cur_frm.doc.enable_checkout) { - cur_frm.toggle_reqd("payment_gateway_account", true); +function toggle_mandatory (frm){ + frm.toggle_reqd("payment_gateway_account", false); + if(frm.doc.enabled && frm.doc.enable_checkout) { + frm.toggle_reqd("payment_gateway_account", true); } } diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json index e828f54878b..e22af9a601d 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json @@ -1,750 +1,182 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-06-19 15:57:32", - "custom": 0, - "description": "Default settings for Shopping Cart", - "docstatus": 0, - "doctype": "DocType", - "document_type": "System", - "editable_grid": 0, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "enabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Enable Shopping Cart", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "", - "fieldname": "display_settings", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Display Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "", - "fieldname": "show_attachments", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Public Attachments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "", - "fieldname": "show_price", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Price", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "show_stock_availability", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Stock Availability", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "show_configure_button", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Configure Button", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "show_contact_us_button", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Contact Us Button", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "show_stock_availability", - "fieldname": "show_quantity_in_website", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Stock Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "show_apply_coupon_code_in_website", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Apply Coupon Code", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "allow_items_not_in_stock", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow items not in stock to be added to cart", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "enabled", - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Prices will not be shown if Price List is not set", - "fieldname": "price_list", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "default_customer_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Customer Group", - "length": 0, - "no_copy": 0, - "options": "Customer Group", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "quotation_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Quotation Series", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval:doc.enable_checkout", - "columns": 0, - "depends_on": "enabled", - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Checkout Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "", - "fieldname": "enable_checkout", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Enable Checkout", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Orders", - "description": "After payment completion redirect user to selected page.", - "fieldname": "payment_success_url", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Success Url", - "length": 0, - "no_copy": 0, - "options": "\nOrders\nInvoices\nMy Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_gateway_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Gateway Account", - "length": 0, - "no_copy": 0, - "options": "Payment Gateway Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-shopping-cart", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2019-10-14 13:54:24.575322", - "modified_by": "Administrator", - "module": "Shopping Cart", - "name": "Shopping Cart Settings", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Website Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 - } \ No newline at end of file + "creation": "2013-06-19 15:57:32", + "description": "Default settings for Shopping Cart", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "enabled", + "display_settings", + "show_attachments", + "show_price", + "show_stock_availability", + "enable_variants", + "show_contact_us_button", + "show_quantity_in_website", + "show_apply_coupon_code_in_website", + "allow_items_not_in_stock", + "section_break_2", + "company", + "price_list", + "column_break_4", + "default_customer_group", + "quotation_series", + "section_break_8", + "enable_checkout", + "payment_success_url", + "column_break_11", + "payment_gateway_account" + ], + "fields": [ + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Enable Shopping Cart" + }, + { + "fieldname": "display_settings", + "fieldtype": "Section Break", + "label": "Display Settings" + }, + { + "default": "0", + "fieldname": "show_attachments", + "fieldtype": "Check", + "label": "Show Public Attachments" + }, + { + "default": "0", + "fieldname": "show_price", + "fieldtype": "Check", + "label": "Show Price" + }, + { + "default": "0", + "fieldname": "show_stock_availability", + "fieldtype": "Check", + "label": "Show Stock Availability" + }, + { + "default": "0", + "fieldname": "show_contact_us_button", + "fieldtype": "Check", + "label": "Show Contact Us Button" + }, + { + "default": "0", + "depends_on": "show_stock_availability", + "fieldname": "show_quantity_in_website", + "fieldtype": "Check", + "label": "Show Stock Quantity" + }, + { + "default": "0", + "fieldname": "show_apply_coupon_code_in_website", + "fieldtype": "Check", + "label": "Show Apply Coupon Code" + }, + { + "default": "0", + "fieldname": "allow_items_not_in_stock", + "fieldtype": "Check", + "label": "Allow items not in stock to be added to cart" + }, + { + "depends_on": "enabled", + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1 + }, + { + "description": "Prices will not be shown if Price List is not set", + "fieldname": "price_list", + "fieldtype": "Link", + "label": "Price List", + "options": "Price List" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "default_customer_group", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Customer Group", + "options": "Customer Group" + }, + { + "fieldname": "quotation_series", + "fieldtype": "Select", + "label": "Quotation Series" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:doc.enable_checkout", + "depends_on": "enabled", + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Checkout Settings" + }, + { + "default": "0", + "fieldname": "enable_checkout", + "fieldtype": "Check", + "label": "Enable Checkout" + }, + { + "default": "Orders", + "description": "After payment completion redirect user to selected page.", + "fieldname": "payment_success_url", + "fieldtype": "Select", + "label": "Payment Success Url", + "options": "\nOrders\nInvoices\nMy Account" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "payment_gateway_account", + "fieldtype": "Link", + "label": "Payment Gateway Account", + "options": "Payment Gateway Account" + }, + { + "default": "0", + "fieldname": "enable_variants", + "fieldtype": "Check", + "label": "Enable Variants" + } + ], + "icon": "fa fa-shopping-cart", + "idx": 1, + "issingle": 1, + "modified": "2020-07-07 02:13:23.175604", + "modified_by": "Administrator", + "module": "Shopping Cart", + "name": "Shopping Cart Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "Website Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "ASC" +} \ No newline at end of file diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py index 3098190383b..c069b90e986 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py @@ -80,6 +80,7 @@ def get_shopping_cart_settings(): return frappe.local.shopping_cart_settings +@frappe.whitelist(allow_guest=True) def is_cart_enabled(): return get_shopping_cart_settings().enabled diff --git a/erpnext/templates/generators/item/item_configure.html b/erpnext/templates/generators/item/item_configure.html index 04f89eca9db..b8b0d98bdc2 100644 --- a/erpnext/templates/generators/item/item_configure.html +++ b/erpnext/templates/generators/item/item_configure.html @@ -2,7 +2,7 @@ {% set cart_settings = shopping_cart.cart_settings %}
    - {% if cart_settings.show_configure_button | int %} + {% if cart_settings.enable_variants | int %}
    ` ); } -}); \ No newline at end of file +}); diff --git a/erpnext/healthcare/doctype/lab_test/lab_test_list.js b/erpnext/healthcare/doctype/lab_test/lab_test_list.js index 1f6a12f935f..2e76bb8ddd8 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test_list.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test_list.js @@ -52,7 +52,7 @@ var create_multiple_dialog = function(listview){ } }, freeze: true, - freeze_message: "Creating Lab Test..." + freeze_message: __("Creating Lab Test...") }); dialog.hide(); } diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index c1b0b18dba4..e6ceb5c0fc3 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -116,7 +116,7 @@ var schedule_inpatient = function(frm) { } }, freeze: true, - freeze_message: "Process Inpatient Scheduling" + freeze_message: __("Process Inpatient Scheduling") }); }; @@ -130,7 +130,7 @@ var schedule_discharge = function(frm) { } }, freeze: true, - freeze_message: "Process Discharge" + freeze_message: __("Process Discharge") }); }; diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.js b/erpnext/hr/doctype/payroll_entry/payroll_entry.js index da25d7574e0..ab4b6e7d0db 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.js @@ -211,7 +211,7 @@ frappe.ui.form.on('Payroll Entry', { }, doc: frm.doc, freeze: true, - freeze_message: 'Validating Employee Attendance...' + freeze_message: __('Validating Employee Attendance...') }); }else{ frm.fields_dict.attendance_detail_html.html(""); @@ -235,7 +235,7 @@ const submit_salary_slip = function (frm) { callback: function() {frm.events.refresh(frm);}, doc: frm.doc, freeze: true, - freeze_message: 'Submitting Salary Slips and creating Journal Entry...' + freeze_message: __('Submitting Salary Slips and creating Journal Entry...') }); }, function() { From b7efef79dce76d7065a878dc3f7a8474f5ba6119 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 27 Jul 2020 14:54:59 +0530 Subject: [PATCH 794/929] fix: Exploded Item Rate (#22816) --- erpnext/manufacturing/doctype/bom/bom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 067a6f7674d..68f796ba1bc 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -539,7 +539,7 @@ class BOM(WebsiteGenerator): 'image' : d.image, 'stock_uom' : d.stock_uom, 'stock_qty' : flt(d.stock_qty), - 'rate' : d.base_rate, + 'rate' : flt(d.base_rate) / flt(d.conversion_factor), 'include_item_in_manufacturing': d.include_item_in_manufacturing })) From b2eddedfb14ad86cef7500a441006d575dec4799 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 29 Jul 2020 17:19:11 +0530 Subject: [PATCH 795/929] fix: change owner fieldtype to Link in Lead Owner Efficiency Report (#22840) --- .../crm/report/lead_owner_efficiency/lead_owner_efficiency.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py index 6172a75fdd8..cb37fb4edfb 100644 --- a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py +++ b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py @@ -17,7 +17,8 @@ def get_columns(): { "fieldname": "lead_owner", "label": _("Lead Owner"), - "fieldtype": "Data", + "fieldtype": "Link", + "options": "User", "width": "130" }, { From 3d2fa59e6d99f3f38eb3d45bc505501647a35a59 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 30 Jul 2020 13:27:47 +0530 Subject: [PATCH 796/929] fix(Education): course wise assessment report labels (#22805) * fix: add labels to chart datasets in course wise assessment * fix: change label for final grade Co-authored-by: Marica --- .../course_wise_assessment_report.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.py b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.py index ce581486ec3..1043e5bd45b 100644 --- a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.py +++ b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.py @@ -42,7 +42,7 @@ def execute(filters=None): # create the list of possible grades if student_row[scrub_criteria] not in grades: grades.append(student_row[scrub_criteria]) - + # create the dict of for gradewise analysis if student_row[scrub_criteria] not in grade_wise_analysis[criteria]: grade_wise_analysis[criteria][student_row[scrub_criteria]] = 1 @@ -101,7 +101,7 @@ def get_formatted_result(args, get_assessment_criteria=False, get_course=False, # create the nested dictionary structure as given below: # ..... - # "Total Score" -> assessment criteria used for totaling and args.assessment_group -> for totaling all the assesments + # "Final Grade" -> assessment criteria used for totaling and args.assessment_group -> for totaling all the assesments student_details = {} formatted_assessment_result = defaultdict(dict) @@ -123,13 +123,13 @@ def get_formatted_result(args, get_assessment_criteria=False, get_course=False, formatted_assessment_result[result.student][result.course][assessment_group]\ [assessment_criteria]["grade"] = tmp_grade - # create the assessment criteria "Total Score" with the sum of all the scores of the assessment criteria in a given assessment group + # create the assessment criteria "Final Grade" with the sum of all the scores of the assessment criteria in a given assessment group def add_total_score(result, assessment_group): - if "Total Score" not in formatted_assessment_result[result.student][result.course][assessment_group]: - formatted_assessment_result[result.student][result.course][assessment_group]["Total Score"] = frappe._dict({ - "assessment_criteria": "Total Score", "maximum_score": result.maximum_score, "score": result.score, "grade": result.grade}) + if "Final Grade" not in formatted_assessment_result[result.student][result.course][assessment_group]: + formatted_assessment_result[result.student][result.course][assessment_group]["Final Grade"] = frappe._dict({ + "assessment_criteria": "Final Grade", "maximum_score": result.maximum_score, "score": result.score, "grade": result.grade}) else: - add_score_and_recalculate_grade(result, assessment_group, "Total Score") + add_score_and_recalculate_grade(result, assessment_group, "Final Grade") for result in assessment_result: if result.student not in student_details: @@ -152,7 +152,7 @@ def get_formatted_result(args, get_assessment_criteria=False, get_course=False, elif create_total_dict: if get_all_assessment_groups: formatted_assessment_result[result.student][result.course][result.assessment_group]\ - [result.assessment_criteria] = assessment_criteria_details + [result.assessment_criteria] = assessment_criteria_details if not formatted_assessment_result[result.student][result.course][args.assessment_group]: formatted_assessment_result[result.student][result.course][args.assessment_group] = defaultdict(dict) formatted_assessment_result[result.student][result.course][args.assessment_group]\ @@ -166,7 +166,7 @@ def get_formatted_result(args, get_assessment_criteria=False, get_course=False, add_total_score(result, args.assessment_group) total_maximum_score = formatted_assessment_result[result.student][result.course][args.assessment_group]\ - ["Total Score"]["maximum_score"] + ["Final Grade"]["maximum_score"] if get_assessment_criteria: assessment_criteria_dict[result.assessment_criteria] = formatted_assessment_result[result.student][result.course]\ [args.assessment_group][result.assessment_criteria]["maximum_score"] @@ -174,7 +174,7 @@ def get_formatted_result(args, get_assessment_criteria=False, get_course=False, course_dict[result.course] = total_maximum_score if get_assessment_criteria and total_maximum_score: - assessment_criteria_dict["Total Score"] = total_maximum_score + assessment_criteria_dict["Final Grade"] = total_maximum_score return { "student_details": student_details, @@ -220,7 +220,7 @@ def get_chart_data(grades, criteria_list, kounter): datasets = [] for grade in grades: - tmp = frappe._dict({"values":[], "title": grade}) + tmp = frappe._dict({"name": grade, "values":[]}) for criteria in criteria_list: if grade in kounter[criteria]: tmp["values"].append(kounter[criteria][grade]) From a9cfe0c95c28a83c2fd24fa52730d6dabdd8025d Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 3 Aug 2020 14:56:46 +0530 Subject: [PATCH 797/929] fix: Misleading description in Warehouse (#22879) --- erpnext/stock/doctype/warehouse/warehouse.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json index 909d8216308..3df2506884b 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.json +++ b/erpnext/stock/doctype/warehouse/warehouse.json @@ -44,7 +44,6 @@ "oldfieldtype": "Section Break" }, { - "description": "If blank, parent Warehouse Account or company default will be considered", "fieldname": "warehouse_name", "fieldtype": "Data", "label": "Warehouse Name", @@ -234,7 +233,8 @@ "icon": "fa fa-building", "idx": 1, "is_tree": 1, - "modified": "2020-07-22 14:46:37.650475", + "links": [], + "modified": "2020-08-03 11:19:35.943330", "modified_by": "Administrator", "module": "Stock", "name": "Warehouse", From ec46b8c73b0a7f92647c008014d5c71fcd8ea732 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 3 Aug 2020 15:39:29 +0530 Subject: [PATCH 798/929] fix: Bank Clearance of POS purchase invoice (#22884) --- .../bank_reconciliation.py | 22 +- .../purchase_invoice/purchase_invoice.json | 8 +- .../sales_invoice_payment.json | 379 ++++-------------- 3 files changed, 100 insertions(+), 309 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 48fd154a4db..0b33b2e5cd7 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -60,12 +60,13 @@ class BankReconciliation(Document): """.format(condition=condition), {"account": self.account, "from":self.from_date, "to": self.to_date, "bank_account": self.bank_account}, as_dict=1) - pos_entries = [] + + pos_sales_invoices, pos_purchase_invoices = [], [] if self.include_pos_transactions: - pos_entries = frappe.db.sql(""" + pos_sales_invoices = frappe.db.sql(""" select "Sales Invoice Payment" as payment_document, sip.name as payment_entry, sip.amount as debit, - si.posting_date, si.debit_to as against_account, sip.clearance_date, + si.posting_date, si.customer as against_account, sip.clearance_date, account.account_currency, 0 as credit from `tabSales Invoice Payment` sip, `tabSales Invoice` si, `tabAccount` account where @@ -75,7 +76,20 @@ class BankReconciliation(Document): si.posting_date ASC, si.name DESC """, {"account":self.account, "from":self.from_date, "to":self.to_date}, as_dict=1) - entries = sorted(list(payment_entries)+list(journal_entries+list(pos_entries)), + pos_purchase_invoices = frappe.db.sql(""" + select + "Purchase Invoice" as payment_document, pi.name as payment_entry, pi.paid_amount as credit, + pi.posting_date, pi.supplier as against_account, pi.clearance_date, + account.account_currency, 0 as debit + from `tabPurchase Invoice` pi, `tabAccount` account + where + pi.cash_bank_account=%(account)s and pi.docstatus=1 and account.name = pi.cash_bank_account + and pi.posting_date >= %(from)s and pi.posting_date <= %(to)s + order by + pi.posting_date ASC, pi.name DESC + """, {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) + + entries = sorted(list(payment_entries) + list(journal_entries + list(pos_sales_invoices) + list(pos_purchase_invoices)), key=lambda k: k['posting_date'] or getdate(nowdate())) self.set('payment_entries', []) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index f65479ca01d..47769021e27 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -962,8 +962,10 @@ { "fieldname": "clearance_date", "fieldtype": "Date", - "hidden": 1, - "label": "Clearance Date" + "label": "Clearance Date", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 }, { "fieldname": "col_br_payments", @@ -1298,7 +1300,7 @@ "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, - "modified": "2020-07-01 12:41:54.851217", + "modified": "2020-08-03 13:08:19.611710", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json index 52cf810ae4c..f5398579581 100644 --- a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json +++ b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json @@ -1,314 +1,89 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-05-08 23:49:38.842621", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "creation": "2016-05-08 23:49:38.842621", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "default", + "mode_of_payment", + "amount", + "column_break_3", + "account", + "type", + "base_amount", + "clearance_date" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:parent.doctype == 'POS Profile'", - "fetch_if_empty": 0, - "fieldname": "default", - "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": "Default", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:parent.doctype == 'POS Profile'", + "fieldname": "default", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Default" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "mode_of_payment", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Mode of Payment", - "length": 0, - "no_copy": 0, - "options": "Mode of Payment", - "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 - }, + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Mode of Payment", + "options": "Mode of Payment", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "eval:parent.doctype == 'Sales Invoice'", - "fetch_if_empty": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:parent.doctype == 'Sales Invoice'", + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "currency", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "account", + "fieldtype": "Link", + "label": "Account", + "options": "Account", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "mode_of_payment.type", - "fetch_if_empty": 0, - "fieldname": "type", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Type", - "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 - }, + "fetch_from": "mode_of_payment.type", + "fieldname": "type", + "fieldtype": "Read Only", + "label": "Type" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "base_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": "Base Amount (Company Currency)", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "base_amount", + "fieldtype": "Currency", + "label": "Base Amount (Company Currency)", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "clearance_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": "Clearance Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "clearance_date", + "fieldtype": "Date", + "label": "Clearance Date", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-03-19 14:54:56.524556", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Sales Invoice Payment", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "modified": "2020-08-03 13:07:49.260534", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Sales Invoice Payment", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file From 4f6a25539a1e004de24e9d19ae04d366c3aeb8cf Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 3 Aug 2020 15:51:39 +0530 Subject: [PATCH 799/929] fix: TypeError while concatenating account number and name in COA (#22886) --- .../doctype/account/chart_of_accounts/chart_of_accounts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py index 1bf9196a4f7..0e3b24cda3d 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py +++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py @@ -225,7 +225,7 @@ def build_tree_from_json(chart_template, chart_data=None): account['parent_account'] = parent account['expandable'] = True if identify_is_group(child) else False - account['value'] = (child.get('account_number') + ' - ' + account_name) \ + account['value'] = (cstr(child.get('account_number')).strip() + ' - ' + account_name) \ if child.get('account_number') else account_name accounts.append(account) _import_accounts(child, account['value']) From 9afebd311f7c78c5a02282193b9585eca3911c4f Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 3 Aug 2020 20:39:41 +0530 Subject: [PATCH 800/929] fix: Reposition description in Warehouse (#22896) --- erpnext/stock/doctype/warehouse/warehouse.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json index 3df2506884b..6e40bc53d07 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.json +++ b/erpnext/stock/doctype/warehouse/warehouse.json @@ -84,6 +84,7 @@ "fieldtype": "Column Break" }, { + "description": "If blank, parent Warehouse Account or company default will be considered in transactions", "fieldname": "account", "fieldtype": "Link", "label": "Account", @@ -234,7 +235,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-08-03 11:19:35.943330", + "modified": "2020-08-03 18:41:52.442502", "modified_by": "Administrator", "module": "Stock", "name": "Warehouse", From 02fcf569e6490c80d1947fd7652cb4cc7450dad2 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 3 Aug 2020 20:41:37 +0530 Subject: [PATCH 801/929] fix: SQL query in accounts receivable, payable reports (#22891) --- .../report/accounts_receivable/accounts_receivable.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 724e8b74437..04fc33220d9 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -643,8 +643,10 @@ class ReceivablePayableReport(object): account_type = "Receivable" if self.party_type == "Customer" else "Payable" accounts = [d.name for d in frappe.get_all("Account", filters={"account_type": account_type, "company": self.filters.company})] - conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts))) - values += accounts + + if accounts: + conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts))) + values += accounts def add_customer_filters(self, conditions, values): if self.filters.get("customer_group"): From d2593f0af54b74fb7d37f5aa720da740a667982a Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 4 Aug 2020 19:29:56 +0530 Subject: [PATCH 802/929] fix: set half day date None if half day is unchecked (#22905) --- erpnext/hr/doctype/leave_application/leave_application.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index df4cf71b776..7b274119244 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -39,6 +39,9 @@ frappe.ui.form.on("Leave Application", { validate: function(frm) { frm.toggle_reqd("half_day_date", frm.doc.half_day == 1); + if (frm.doc.half_day == 0){ + frm.doc.half_day_date = ""; + } }, make_dashboard: function(frm) { From 97e96cec517a01ae2b46e16c6a90ee5c0af158bf Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 5 Aug 2020 17:10:50 +0530 Subject: [PATCH 803/929] fix: get_regional_address_details missing positional arg 'company' (#22920) --- erpnext/regional/india/taxes.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js index 1e59032db10..44891a76a0b 100644 --- a/erpnext/regional/india/taxes.js +++ b/erpnext/regional/india/taxes.js @@ -10,6 +10,8 @@ erpnext.setup_auto_gst_taxation = (doctype) => { frm.trigger('get_tax_template'); }, get_tax_template: function(frm) { + if (!frm.doc.company) return; + let party_details = { 'shipping_address': frm.doc.shipping_address || '', 'shipping_address_name': frm.doc.shipping_address_name || '', From 10604664d403ae272542e7a85f14eeb88bea36c0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 19 Jul 2020 21:20:28 +0530 Subject: [PATCH 804/929] fix: Error due to commma in Pricing rule name --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 6 ++++-- erpnext/accounts/doctype/pricing_rule/utils.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index b7c6d4a9728..fcc467536d2 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -247,7 +247,9 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa if pricing_rules: rules = [] + print(pricing_rules, "$$$$$$") for pricing_rule in pricing_rules: + print(pricing_rule) if not pricing_rule: continue if isinstance(pricing_rule, string_types): @@ -280,7 +282,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa item_details.has_pricing_rule = 1 - item_details.pricing_rules = ','.join([d.pricing_rule for d in rules]) + item_details.pricing_rules = frappe.as_json([d.pricing_rule for d in rules]) if not doc: return item_details @@ -370,7 +372,7 @@ def set_discount_amount(rate, item_details): def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None): from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rule_items - for d in pricing_rules.split(','): + for d in json.loads(pricing_rules): if not d or not frappe.db.exists("Pricing Rule", d): continue pricing_rule = frappe.get_cached_doc('Pricing Rule', d) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index dc27cfb3342..7d20208fc79 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -471,7 +471,7 @@ def apply_pricing_rule_on_transaction(doc): doc.set_missing_values() def get_applied_pricing_rules(item_row): - return (item_row.get("pricing_rules").split(',') + return (json.loads(item_row.get("pricing_rules")) if item_row.get("pricing_rules") else []) def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): From 579167d1ded5ed788428e94654a388744701bd10 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 19 Jul 2020 21:40:33 +0530 Subject: [PATCH 805/929] fix: Remove print statement --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index fcc467536d2..bb4f5bc602c 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -247,9 +247,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa if pricing_rules: rules = [] - print(pricing_rules, "$$$$$$") for pricing_rule in pricing_rules: - print(pricing_rule) if not pricing_rule: continue if isinstance(pricing_rule, string_types): From e6ec549c8129c059a3fc8e5b67bd51003fe59fa2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 19 Jul 2020 18:05:23 +0530 Subject: [PATCH 806/929] fix: Multi currency payment reconciliation --- .../payment_reconciliation.js | 4 + .../payment_reconciliation.py | 10 +- .../payment_reconciliation_invoice.json | 242 +++++------------- .../payment_reconciliation_payment.json | 17 +- .../sales_invoice_item.json | 3 +- erpnext/accounts/utils.py | 6 +- erpnext/controllers/accounts_controller.py | 12 +- 7 files changed, 109 insertions(+), 185 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index d3992d51115..355fe96c967 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -73,6 +73,10 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext }; } }); + + this.frm.set_value('party_type', ''); + this.frm.set_value('party', ''); + this.frm.set_value('receivable_payable_account', ''); }, refresh: function() { diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 8eaad7acd4b..368c358fe8e 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -48,7 +48,8 @@ class PaymentReconciliation(Document): select "Journal Entry" as reference_type, t1.name as reference_name, t1.posting_date, t1.remark as remarks, t2.name as reference_row, - {dr_or_cr} as amount, t2.is_advance + {dr_or_cr} as amount, t2.is_advance, + t2.account_currency as currency from `tabJournal Entry` t1, `tabJournal Entry Account` t2 where @@ -88,7 +89,8 @@ class PaymentReconciliation(Document): if self.party_type == 'Customer' else "Purchase Invoice") return frappe.db.sql(""" SELECT `tab{doc}`.name as reference_name, %(voucher_type)s as reference_type, - (sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount + (sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount, + account_currency as currency FROM `tab{doc}`, `tabGL Entry` WHERE (`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no) @@ -141,6 +143,7 @@ class PaymentReconciliation(Document): ent.invoice_number = e.get('voucher_no') ent.invoice_date = e.get('posting_date') ent.amount = flt(e.get('invoice_amount')) + ent.currency = e.get('currency') ent.outstanding_amount = e.get('outstanding_amount') def reconcile(self, args): @@ -269,11 +272,14 @@ def reconcile_dr_cr_note(dr_cr_notes, company): reconcile_dr_or_cr = ('debit_in_account_currency' if d.dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency') + company_currency = erpnext.get_company_currency(company) + jv = frappe.get_doc({ "doctype": "Journal Entry", "voucher_type": voucher_type, "posting_date": today(), "company": company, + "multi_currency": 1 if d.currency != company_currency else 0, "accounts": [ { 'account': d.account, diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json index ce7ce98edbe..35f756e2cd0 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json +++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json @@ -1,183 +1,79 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2014-07-09 16:14:23.672922", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "creation": "2014-07-09 16:14:23.672922", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "invoice_type", + "invoice_number", + "invoice_date", + "col_break1", + "amount", + "outstanding_amount", + "currency" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "invoice_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Invoice Type", - "length": 0, - "no_copy": 0, - "options": "Sales Invoice\nPurchase Invoice\nJournal Entry", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "invoice_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Invoice Type", + "options": "Sales Invoice\nPurchase Invoice\nJournal Entry", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "invoice_number", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Invoice Number", - "length": 0, - "no_copy": 0, - "options": "invoice_type", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "invoice_number", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Invoice Number", + "options": "invoice_type", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "invoice_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Invoice Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "invoice_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Invoice Date", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "col_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "col_break1", + "fieldtype": "Column Break" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "currency", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "outstanding_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Outstanding Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "outstanding_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Outstanding Amount", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency" } - ], - "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": "2016-07-11 03:28:03.588476", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Payment Reconciliation Invoice", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-07-19 17:33:24.446267", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Reconciliation Invoice", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json index 018bfd028a6..e88e0f2ad73 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -1,7 +1,9 @@ { + "actions": [], "creation": "2014-07-09 16:13:35.452759", "doctype": "DocType", "editable_grid": 1, + "engine": "InnoDB", "field_order": [ "reference_type", "reference_name", @@ -16,7 +18,8 @@ "difference_account", "difference_amount", "sec_break1", - "remark" + "remark", + "currency" ], "fields": [ { @@ -73,6 +76,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", + "options": "currency", "read_only": 1 }, { @@ -81,6 +85,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Allocated amount", + "options": "currency", "reqd": 1 }, { @@ -106,16 +111,24 @@ "fieldname": "difference_amount", "fieldtype": "Currency", "label": "Difference Amount", + "options": "currency", "print_hide": 1, "read_only": 1 }, { "fieldname": "section_break_10", "fieldtype": "Section Break" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency" } ], "istable": 1, - "modified": "2019-06-24 00:08:11.150796", + "links": [], + "modified": "2020-07-19 11:25:35.506232", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Payment", diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 49deebd3c6c..227e2071bf3 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -797,7 +797,8 @@ ], "idx": 1, "istable": 1, - "modified": "2020-06-30 16:47:37.650996", + "links": [], + "modified": "2020-07-18 12:24:41.749986", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 5647c6f9dca..8d4ca66b044 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -658,7 +658,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None, filters invoice_list = frappe.db.sql(""" select voucher_no, voucher_type, posting_date, due_date, - ifnull(sum({dr_or_cr}), 0) as invoice_amount + ifnull(sum({dr_or_cr}), 0) as invoice_amount, + account_currency as currency from `tabGL Entry` where @@ -715,7 +716,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None, filters 'invoice_amount': flt(d.invoice_amount), 'payment_amount': payment_amount, 'outstanding_amount': outstanding_amount, - 'due_date': d.due_date + 'due_date': d.due_date, + 'currency': d.currency }) ) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index b41ce7379a1..797adfce8e0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1011,6 +1011,7 @@ def get_advance_journal_entries(party_type, party, party_account, amount_field, def get_advance_payment_entries(party_type, party, party_account, order_doctype, order_list=None, include_unallocated=True, against_all_orders=False, limit=None): party_account_field = "paid_from" if party_type == "Customer" else "paid_to" + currency_field = "paid_from_account_currency" if party_type == "Customer" else "paid_to_account_currency" payment_type = "Receive" if party_type == "Customer" else "Pay" payment_entries_against_order, unallocated_payment_entries = [], [] limit_cond = "limit %s" % limit if limit else "" @@ -1027,14 +1028,15 @@ def get_advance_payment_entries(party_type, party, party_account, order_doctype, select "Payment Entry" as reference_type, t1.name as reference_name, t1.remarks, t2.allocated_amount as amount, t2.name as reference_row, - t2.reference_name as against_order, t1.posting_date + t2.reference_name as against_order, t1.posting_date, + t1.{0} as currency from `tabPayment Entry` t1, `tabPayment Entry Reference` t2 where - t1.name = t2.parent and t1.{0} = %s and t1.payment_type = %s + t1.name = t2.parent and t1.{1} = %s and t1.payment_type = %s and t1.party_type = %s and t1.party = %s and t1.docstatus = 1 - and t2.reference_doctype = %s {1} - order by t1.posting_date {2} - """.format(party_account_field, reference_condition, limit_cond), + and t2.reference_doctype = %s {2} + order by t1.posting_date {3} + """.format(currency_field, party_account_field, reference_condition, limit_cond), [party_account, payment_type, party_type, party, order_doctype] + order_list, as_dict=1) From 024c78f522f372a6ad35bd5d44fdf298dcd70c4c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 19 Jul 2020 18:13:04 +0530 Subject: [PATCH 807/929] fix: Hide currency link fields --- .../payment_reconciliation_invoice.json | 3 ++- .../payment_reconciliation_payment.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json index 35f756e2cd0..6a79a85c348 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json +++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json @@ -60,13 +60,14 @@ { "fieldname": "currency", "fieldtype": "Link", + "hidden": 1, "label": "Currency", "options": "Currency" } ], "istable": 1, "links": [], - "modified": "2020-07-19 17:33:24.446267", + "modified": "2020-07-19 18:12:27.964073", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Invoice", diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json index e88e0f2ad73..925a6f10a5e 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -122,13 +122,14 @@ { "fieldname": "currency", "fieldtype": "Link", + "hidden": 1, "label": "Currency", "options": "Currency" } ], "istable": 1, "links": [], - "modified": "2020-07-19 11:25:35.506232", + "modified": "2020-07-19 18:12:41.682347", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Payment", From dd64cd1512739da4c3cebb7e260fd980ff117fa0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 8 Jul 2020 11:27:25 +0530 Subject: [PATCH 808/929] fix: Add default cost center in payment reconciliation JV --- .../payment_reconciliation/payment_reconciliation.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 8eaad7acd4b..35d8d34c518 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -281,7 +281,8 @@ def reconcile_dr_cr_note(dr_cr_notes, company): 'party_type': d.party_type, d.dr_or_cr: abs(d.allocated_amount), 'reference_type': d.against_voucher_type, - 'reference_name': d.against_voucher + 'reference_name': d.against_voucher, + 'cost_center': erpnext.get_default_cost_center(company) }, { 'account': d.account, @@ -290,7 +291,8 @@ def reconcile_dr_cr_note(dr_cr_notes, company): reconcile_dr_or_cr: (abs(d.allocated_amount) if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)), 'reference_type': d.voucher_type, - 'reference_name': d.voucher_no + 'reference_name': d.voucher_no, + 'cost_center': erpnext.get_default_cost_center(company) } ] }) From b3cdd91d65cd731c0d458d5225320826cd45d655 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 20 Jul 2020 10:12:17 +0530 Subject: [PATCH 809/929] fix: Tests --- erpnext/controllers/taxes_and_totals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 82f820c4252..7534bf43a69 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -593,7 +593,7 @@ class calculate_taxes_and_totals(object): base_rate_with_margin = 0.0 if item.price_list_rate: if item.pricing_rules and not self.doc.ignore_pricing_rule: - for d in item.pricing_rules.split(','): + for d in json.loads(item.pricing_rules): pricing_rule = frappe.get_cached_doc('Pricing Rule', d) if (pricing_rule.margin_type == 'Amount' and pricing_rule.currency == self.doc.currency)\ From 61b9727fa2cd20afb537408c712ff0191e270288 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 6 Aug 2020 16:47:43 +0530 Subject: [PATCH 810/929] fix: unbound error in product configurator --- erpnext/portal/product_configurator/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 69ff1dcccaa..6f9a627d534 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -261,12 +261,14 @@ def get_next_attribute_and_values(item_code, selected_attributes): if exact_match: data = get_product_info_for_website(exact_match[0]) product_info = data.product_info + if product_info: + product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock) if not data.cart_settings.show_price: product_info = None else: product_info = None - product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock) + return { 'next_attribute': next_attribute, From 3619628e606d1c2c161f8c7bb7c1c7014d1d9e44 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 6 Aug 2020 17:08:14 +0530 Subject: [PATCH 811/929] fix: handle product_info null --- erpnext/templates/generators/item/item_configure.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js index 163c955c566..284eb252186 100644 --- a/erpnext/templates/generators/item/item_configure.js +++ b/erpnext/templates/generators/item/item_configure.js @@ -194,9 +194,16 @@ class ItemConfigure { filtered_items[0] : ''; // Allow Add to Cart if adding out of stock items enabled in Shopping Cart else check stock. - const in_stock = product_info.allow_items_not_in_stock ? 1 : product_info.in_stock; - const add_to_cart = `${__('Add to cart')}`; - const product_action = in_stock ? add_to_cart : `${__('Not in Stock')}`; + var in_stock; + var add_to_cart; + var product_action; + if (product_info) { + in_stock = product_info.allow_items_not_in_stock ? 1 : product_info.in_stock; + add_to_cart = `${__('Add to cart')}`; + product_action = in_stock ? add_to_cart : `${__('Not in Stock')}`; + } else { + product_info = ''; + } const item_add_to_cart = one_item ? `